├── .editorconfig ├── .eslintrc ├── .gitignore ├── README.md ├── babel.config.js ├── config ├── dev.js ├── index.js └── prod.js ├── dede └── appclient.php ├── demo ├── 001.png ├── 002.png ├── 003.png ├── 004.png ├── 005.png ├── 006.png ├── 007.png ├── 008.png ├── 009.png ├── 010.png ├── 011.png ├── 012.png ├── 013.png └── 014.png ├── global.d.ts ├── jest.config.ts ├── package.json ├── project.config.json ├── src ├── api.ts ├── app.config.ts ├── app.css ├── app.scss ├── app.ts ├── assets │ ├── about-selected.png │ ├── about.png │ ├── article-selected.png │ ├── article.png │ ├── contact-selected.png │ ├── contact.png │ ├── default.png │ ├── home-selected.png │ ├── home.png │ ├── nav-1.png │ ├── nav-2.png │ ├── nav-3.png │ ├── nav-4.png │ ├── phone.png │ ├── shop-selected.png │ └── shop.png ├── components │ ├── Banner │ │ ├── index.scss │ │ └── index.tsx │ ├── article │ │ ├── index.scss │ │ └── index.tsx │ ├── cell │ │ ├── index.scss │ │ └── index.tsx │ ├── container │ │ ├── index.scss │ │ └── index.tsx │ ├── empty │ │ ├── index.scss │ │ └── index.tsx │ ├── footer │ │ ├── index.scss │ │ └── index.tsx │ ├── header │ │ ├── index.scss │ │ └── index.tsx │ ├── product │ │ ├── index.scss │ │ └── index.tsx │ └── richhtml │ │ ├── image │ │ ├── index.scss │ │ └── index.tsx │ │ ├── index.scss │ │ ├── index.tsx │ │ ├── utils │ │ ├── html2Json.ts │ │ └── htmlParser.ts │ │ ├── video │ │ ├── index.scss │ │ └── index.tsx │ │ └── view │ │ ├── index.scss │ │ └── index.tsx ├── config.dist.ts ├── config.ts ├── index.html ├── pages │ ├── about │ │ ├── index.config.ts │ │ ├── index.scss │ │ └── index.tsx │ ├── article │ │ ├── index.config.ts │ │ ├── index.scss │ │ └── index.tsx │ ├── articles │ │ ├── index.config.ts │ │ ├── index.scss │ │ └── index.tsx │ ├── browser │ │ ├── index.config.ts │ │ ├── index.scss │ │ └── index.tsx │ ├── category │ │ ├── index.config.ts │ │ ├── index.scss │ │ └── index.tsx │ ├── contact │ │ ├── index.config.ts │ │ ├── index.scss │ │ └── index.tsx │ ├── index │ │ ├── index.config.ts │ │ ├── index.scss │ │ └── index.tsx │ ├── product │ │ ├── index.config.ts │ │ ├── index.scss │ │ └── index.tsx │ └── products │ │ ├── index.config.ts │ │ ├── index.scss │ │ └── index.tsx ├── project.swan.json ├── request.ts ├── store.ts ├── theme.scss ├── utils.ts ├── var.scss ├── version.ts └── 使用帮助.url ├── tsconfig.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["taro/react"], 3 | "rules": { 4 | "react/jsx-uses-react": "off", 5 | "react/react-in-jsx-scope": "off" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | .temp/ 3 | .rn_temp/ 4 | node_modules/ 5 | .DS_Store 6 | .swc/ 7 | output/ 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dedecms网站配套小程序 2 | 3 | 说明:该小程序源码同时支持百度智能小程序,微信小程序,支付宝小程序,头条小程序等,下面以百度小程序为例子展开配置说明 4 | 5 | 6 | ## dedecms网站配套百度小程序使用指南 7 | 8 | ## 一、前言 9 | dedecms网站配套百度小程序,可以直接将dedecms站点的数据传输到百度小程序中,省去使用第三方小程序管理后台的烦恼,不需要重新给百度小程序再发布一遍产品和文章,节省大量的编辑工作。 10 | 11 | ## 二、安装方法 12 | 1. 从github的[Release](https://github.com/fesiong/dedeapp/releases) 中下载小程序代码,并解压。 13 | 2. 从github上下载[appclient.php](https://github.com/fesiong/dedeapp/blob/master/dede/appclient.php),并上传到网站根目录,访问一次,如果有内容输出,表示正常。 14 | 3. 进入小程序源码,并用百度开发者工具打开预览 15 | 4. 打开小程序源码根目录下的common.js,格式化它,并编辑对应的字段,按照里面的说明和示例填写基本配置信息。(小程序的配置同样可以通过dedecms后台配置,后台配置在后台的系统设置->系统基本参数->站点设置里)。 16 | 5. 点击百度开发者工具->项目信息,填写appid 17 | 6. 到百度小程序后台,添加站点域名为请求域名。 18 | 7. 在百度开发者工具提交发布。 19 | 8. 到百度小程序后台->开发管理中,将刚提交上来的小程序代码提交审核。 20 | 21 | ## 三、百度开发者工具使用 22 | 请自行熟悉,这里不做详细解答 23 | 24 | ## 四、小程序配置 25 | 小程序的配置文件在小程序包的根目录下的common.js,格式化它。初次安装,需要配置必要的信息,才能让小程序正常工作。如果您的织梦站点没有使用产品模块,则可能需要选择停用产品功能。同样的配置也可以通过织梦后台配置。后台的配置在:后台的系统设置->系统基本参数->站点设置 里面。一下大部分配置并不需要操作,默认即可。 26 | 27 | ### 接口设置 28 | 填写API地址:API地址由您的站点地址+appclient.php组成,例如你的站点地址是http://dev.dedecms.com,那么API地址则是http://dev.dedecms.com/appclient.php,请确保这个地址能正常访问到接口文件,接口文件的上传请参照 二、安装方法 29 | 30 | 1. 基本设置 31 | 基本配置信息包括小程序logo、小程序名称,小程序名称显示在小程序首页顶部导航上。配置如图 32 | ![001](https://raw.githubusercontent.com/fesiong/dedeapp/master/demo/001.png) 33 | 34 | 2. 首页TDK设置 35 | 首页TDK是指标题、关键词、描述,同网站首页的TDK一致,是小程序重要指标,务必认真填写。如图 36 | ![002](https://raw.githubusercontent.com/fesiong/dedeapp/master/demo/002.png) 37 | 38 | 3. 联系方式设置 39 | 联系方式包括公司名称、公司地址、联系电话、联系微信,它们会显示在联系我们的页面里,以及各个页面的悬浮按钮。如图 40 | ![003](https://raw.githubusercontent.com/fesiong/dedeapp/master/demo/003.png) 41 | 42 | 4. 内容设置 43 | 内容设置可以设置关于我们、联系我们、首页显示的分类内容、启用产品功能、停用产品功能等。 44 | 关于我们、联系我们需要指定单页id或分类id,优先使用单页id,只有填写了正确的id后,小程序才能正确调用关于我们和联系我们。如图 45 | ![004](https://raw.githubusercontent.com/fesiong/dedeapp/master/demo/004.png) 46 | 小程序首页默认会显示一组分离以及分类的内容列表,正确配置首页显示的分类,可以让首页更美观以及将更有用的信息展示给用户。比如需要将培训视频、学员作品、客户案例展示在首页,他们的分类id分别是1,7,8,那么首页分类则填写1,7,8,它们之间需要使用英文的,隔开。如图 47 | ![005](https://raw.githubusercontent.com/fesiong/dedeapp/master/demo/005.png) 48 | 49 | 6. 启用产品功能 50 | 将配置中的showProduct值设置为1,如果织梦站点使用商品模块来管理产品,则productCategory不需要设置,小程序会自动调用商品模块来获取产品分类和产品详情。如图 51 | ![006](https://raw.githubusercontent.com/fesiong/dedeapp/master/demo/006.png) 52 | 53 | 如果织梦站点使用文章模块来管理产品,则还需要设置productCategory的值为产品分类的顶级分类id。如图 54 | ![007](https://raw.githubusercontent.com/fesiong/dedeapp/master/demo/007.png) 55 | 同时还需要将产品中心的导航添加上。打开app.json,找到首页,在它下方加入产品中心,如图 56 | ![008](https://raw.githubusercontent.com/fesiong/dedeapp/master/demo/008.png) 57 | 还需要将首页导航图标反注释。打开common.js,格式化它。如图 58 | ![009](https://raw.githubusercontent.com/fesiong/dedeapp/master/demo/009.png) 59 | 60 | 7. 停用产品功能 61 | 将配置中的showProduct值设置为0即可。如图 62 | ![010](https://raw.githubusercontent.com/fesiong/dedeapp/master/demo/010.png) 63 | 同时还需要将产品中心的导航移除。打开app.json,找到产品中心代码,并将它们移除,如图 64 | ![011](https://raw.githubusercontent.com/fesiong/dedeapp/master/demo/011.png) 65 | 还需要将首页导航图标注释。打开common.js,格式化它。如图 66 | ![012](https://raw.githubusercontent.com/fesiong/dedeapp/master/demo/012.png) 67 | 68 | 8. 首页幻灯片设置 69 | 配置首页幻灯片可以让小程序更突出,也可以让首屏加载更快。幻灯片同时支持在织梦后台配置,织梦后台的文档,添加幻灯属性后,即可替代config.js的配置。 70 | 每张幻灯片由4个字段组成,model的值可以是article或product,id的值是对应的文章或产品id,title为显示的名称,title可以不填,logo为图片地址。如图 71 | ![013](https://raw.githubusercontent.com/fesiong/dedeapp/master/demo/013.png) 72 | 73 | 9. 首页导航图标设置 74 | 75 | 首页导航图标可以让更重要的信息直达。建议配置不要超过5个。 76 | 每个图标由3个字段组成,url为小程序的链接,title为名称,logo为图片地址,尽量使用网络图片地址。如图 77 | ![014](https://raw.githubusercontent.com/fesiong/dedeapp/master/demo/014.png) 78 | 79 | ## 五、sitemap的生成 80 | 访问你的网址+/appclient.php?a=sitemap 81 | 例如网址是https://www.baidu.com,那么,sitemap地址就是https://www.baidu.com/appclient.php?a=sitemap 82 | 83 | ## 六、技术支持 84 | 微信: websafety 85 | 网站:[织梦小程序](https://www.kandaoni.com) 86 | 87 | 参与讨论:请添加我的微信进微信群讨论 88 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | // babel-preset-taro 更多选项和默认值: 2 | // https://github.com/NervJS/taro/blob/next/packages/babel-preset-taro/README.md 3 | module.exports = { 4 | presets: [ 5 | ['taro', { 6 | framework: 'react', 7 | ts: true 8 | }] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /config/dev.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | NODE_ENV: '"development"' 4 | }, 5 | defineConstants: { 6 | }, 7 | weapp: {}, 8 | h5: { 9 | esnextModules: ['taro-ui'] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | import { defineConfig, type UserConfigExport } from '@tarojs/cli' 2 | import TsconfigPathsPlugin from 'tsconfig-paths-webpack-plugin' 3 | import devConfig from './dev' 4 | import prodConfig from './prod' 5 | 6 | // https://taro-docs.jd.com/docs/next/config#defineconfig-辅助函数 7 | export default defineConfig(async (merge, { command, mode }) => { 8 | const baseConfig: UserConfigExport = { 9 | projectName: 'newtaro', 10 | date: '2023-8-18', 11 | designWidth: 750, 12 | deviceRatio: { 13 | 640: 2.34 / 2, 14 | 750: 1, 15 | 375: 2, 16 | 828: 1.81 / 2 17 | }, 18 | sourceRoot: 'src', 19 | outputRoot: 'dist', 20 | plugins: [], 21 | defineConstants: { 22 | }, 23 | copy: { 24 | patterns: [ 25 | { from: 'src/assets/', to: 'dist/assets/' } 26 | ], 27 | options: { 28 | } 29 | }, 30 | framework: 'react', 31 | compiler: 'webpack5', 32 | cache: { 33 | enable: false // Webpack 持久化缓存配置,建议开启。默认配置请参考:https://docs.taro.zone/docs/config-detail#cache 34 | }, 35 | mini: { 36 | postcss: { 37 | pxtransform: { 38 | enable: true, 39 | config: { 40 | 41 | } 42 | }, 43 | url: { 44 | enable: true, 45 | config: { 46 | limit: 1024 // 设定转换尺寸上限 47 | } 48 | }, 49 | cssModules: { 50 | enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true 51 | config: { 52 | namingPattern: 'module', // 转换模式,取值为 global/module 53 | generateScopedName: '[name]__[local]___[hash:base64:5]' 54 | } 55 | } 56 | }, 57 | webpackChain(chain) { 58 | chain.resolve.plugin('tsconfig-paths').use(TsconfigPathsPlugin) 59 | } 60 | }, 61 | h5: { 62 | publicPath: '/', 63 | staticDirectory: 'static', 64 | output: { 65 | filename: 'js/[name].[hash:8].js', 66 | chunkFilename: 'js/[name].[chunkhash:8].js' 67 | }, 68 | miniCssExtractPluginOption: { 69 | ignoreOrder: true, 70 | filename: 'css/[name].[hash].css', 71 | chunkFilename: 'css/[name].[chunkhash].css' 72 | }, 73 | postcss: { 74 | autoprefixer: { 75 | enable: true, 76 | config: {} 77 | }, 78 | cssModules: { 79 | enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true 80 | config: { 81 | namingPattern: 'module', // 转换模式,取值为 global/module 82 | generateScopedName: '[name]__[local]___[hash:base64:5]' 83 | } 84 | } 85 | }, 86 | webpackChain(chain) { 87 | chain.resolve.plugin('tsconfig-paths').use(TsconfigPathsPlugin) 88 | } 89 | }, 90 | rn: { 91 | appName: 'taroDemo', 92 | postcss: { 93 | cssModules: { 94 | enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true 95 | } 96 | } 97 | } 98 | } 99 | if (process.env.NODE_ENV === 'development') { 100 | // 本地开发构建配置(不混淆压缩) 101 | return merge({}, baseConfig, devConfig) 102 | } 103 | // 生产构建配置(默认开启压缩混淆等) 104 | return merge({}, baseConfig, prodConfig) 105 | }) 106 | -------------------------------------------------------------------------------- /config/prod.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | NODE_ENV: '"production"' 4 | }, 5 | defineConstants: {}, 6 | mini: {}, 7 | h5: { 8 | /** 9 | * 如果h5端编译后体积过大,可以使用webpack-bundle-analyzer插件对打包体积进行分析。 10 | * 参考代码如下: 11 | * webpackChain (chain) { 12 | * chain.plugin('analyzer') 13 | * .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, []) 14 | * } 15 | */ 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /dede/appclient.php: -------------------------------------------------------------------------------- 1 | run(); 18 | 19 | class appClient 20 | { 21 | private $configPath; 22 | private $config; 23 | 24 | public function __construct() 25 | { 26 | $this->configPath = APP_PATH . 'appclient.config.php'; 27 | if (!file_exists($this->configPath)) { 28 | //配置不存在,检测并写入配置 29 | $provider = $this->checkProvider(); 30 | if (!$provider) { 31 | showMessage("无法判断站点框架,接口无法正常工作。"); 32 | } 33 | $this->checkConfig($provider); 34 | } 35 | $this->config = include($this->configPath); 36 | } 37 | 38 | public function run() 39 | { 40 | $appAction = new appAction($this->config); 41 | $appAction->handle(); 42 | } 43 | 44 | private function checkProvider() 45 | { 46 | $provider = ''; 47 | if (file_exists(APP_PATH . 'index.php')) { 48 | $content = file_get_contents(APP_PATH . 'index.php'); 49 | if (strpos($content, 'DEDEINC') !== false) { 50 | $provider = 'dedecms'; 51 | } elseif (strpos($content, 'WordPress') !== false) { 52 | $provider = 'wordpress'; 53 | } elseif (strpos($content, 'PHPCMS') !== false) { 54 | $provider = 'phpcms'; 55 | } elseif (strpos($content, 'Zend') !== false) { 56 | if(file_exists(APP_PATH . 'click_cnt.php')){ 57 | $provider = 'nitc'; 58 | } 59 | } 60 | } else if (file_exists(APP_PATH . 'd/') && file_exists(APP_PATH . 'e/')) { 61 | $provider = 'empire'; 62 | } else if (file_exists(APP_PATH . 'plus/')) { 63 | $provider = 'dedecms'; 64 | } 65 | 66 | return $provider; 67 | } 68 | 69 | private function checkConfig($provider) 70 | { 71 | switch ($provider) { 72 | case 'dedecms': 73 | $config = $this->checkDedecms(); 74 | break; 75 | case 'phpcms': 76 | $config = $this->checkPhpcms(); 77 | break; 78 | case 'wordpress': 79 | $config = $this->checkWordpress(); 80 | break; 81 | case 'empire': 82 | $config = $this->checkEmpire(); 83 | break; 84 | case 'nitc': 85 | $config = $this->checkNitc(); 86 | break; 87 | } 88 | $this->writeConfig($config); 89 | } 90 | 91 | private function checkDedecms() 92 | { 93 | $configFile = APP_PATH . "data/common.inc.php"; 94 | if (!file_exists($configFile)) { 95 | $dir_handle = opendir(APP_PATH); 96 | while (($file = readdir($dir_handle)) !== false) { 97 | if (substr($file, 0, 1) !== '.' AND is_dir(APP_PATH . $file)) { 98 | $dir_handle2 = opendir(APP_PATH . $file); 99 | while (($file2 = readdir($dir_handle2)) !== false) { 100 | if ($file2 === 'common.inc.php') { 101 | $filePath = APP_PATH . $file . '/' . $file2; 102 | $content = file_get_contents($filePath); 103 | if (strpos($content, '//数据库连接信息') !== false) { 104 | $configFile = $filePath; 105 | break 2; 106 | } 107 | } 108 | } 109 | closedir($dir_handle2); 110 | } 111 | } 112 | closedir($dir_handle); 113 | } 114 | if (!file_exists($configFile)) { 115 | showMessage("无法获取配置文件,接口无法正常工作"); 116 | } 117 | $cfg_dbhost = $cfg_dbuser = $cfg_dbpwd = $cfg_dbname = $cfg_db_language = $cfg_dbprefix = ''; 118 | require_once($configFile); 119 | $hostArr = explode(":", $cfg_dbhost); 120 | $config = array( 121 | "provider" => 'dedecms', 122 | "database" => array( 123 | 'host' => $hostArr[0], 124 | 'port' => $hostArr[1] ? $hostArr[1] : '3306', 125 | 'user' => $cfg_dbuser, 126 | 'password' => $cfg_dbpwd, 127 | 'database' => $cfg_dbname, 128 | 'charset' => $cfg_db_language, 129 | 'prefix' => $cfg_dbprefix 130 | ), 131 | ); 132 | 133 | //写入一些配置 134 | $provider = new provider($config); 135 | $provider->initApp(); 136 | 137 | return $config; 138 | } 139 | 140 | private function checkPhpcms() 141 | { 142 | $configFile = APP_PATH . "caches/configs/database.php"; 143 | if (!file_exists($configFile)) { 144 | showMessage("无法获取配置文件,接口无法正常工作"); 145 | } 146 | $fileConfig = include($configFile); 147 | $config = array( 148 | "provider" => 'phpcms', 149 | "database" => array( 150 | 'host' => $fileConfig['default']['hostname'], 151 | 'port' => $fileConfig['default']['port'], 152 | 'user' => $fileConfig['default']['username'], 153 | 'password' => $fileConfig['default']['password'], 154 | 'database' => $fileConfig['default']['database'], 155 | 'charset' => $fileConfig['default']['charset'], 156 | 'prefix' => $fileConfig['default']['tablepre'] 157 | ), 158 | ); 159 | 160 | return $config; 161 | } 162 | 163 | private function checkWordpress() 164 | { 165 | $configFile = APP_PATH . "wp-config.php"; 166 | if (!file_exists($configFile)) { 167 | showMessage("无法获取配置文件,接口无法正常工作"); 168 | } 169 | $table_prefix = ''; 170 | require_once($configFile); 171 | $hostArr = explode(":", DB_HOST); 172 | $config = array( 173 | "provider" => 'wordpress', 174 | "database" => array( 175 | 'host' => $hostArr[0], 176 | 'port' => $hostArr[1] ? $hostArr[1] : '3306', 177 | 'user' => DB_USER, 178 | 'password' => DB_PASSWORD, 179 | 'database' => DB_NAME, 180 | 'charset' => DB_CHARSET, 181 | 'prefix' => $table_prefix 182 | ), 183 | ); 184 | 185 | return $config; 186 | } 187 | 188 | private function checkEmpire() 189 | { 190 | define('InEmpireCMS', true); 191 | $configFile = APP_PATH . "e/config/config.php"; 192 | if (!file_exists($configFile)) { 193 | showMessage("无法获取配置文件,接口无法正常工作"); 194 | } 195 | $ecms_config = array(); 196 | require_once($configFile); 197 | $config = array( 198 | "provider" => 'wordpress', 199 | "database" => array( 200 | 'host' => $ecms_config['db']['dbserver'], 201 | 'port' => $ecms_config['db']['dbport'] ? $ecms_config['db']['dbport'] : '3306', 202 | 'user' => $ecms_config['db']['dbusername'], 203 | 'password' => $ecms_config['db']['dbpassword'], 204 | 'database' => $ecms_config['db']['dbname'], 205 | 'charset' => $ecms_config['db']['dbchar'], 206 | 'prefix' => $ecms_config['db']['dbtbpre'] 207 | ), 208 | ); 209 | 210 | return $config; 211 | } 212 | 213 | 214 | private function checkNitc() 215 | { 216 | $configFile = APP_PATH . "config.php"; 217 | if (!file_exists($configFile)) { 218 | showMessage("无法获取配置文件,接口无法正常工作"); 219 | } 220 | $db_host = $db_user = $db_pass = $db_name = $prefix = ''; 221 | require_once($configFile); 222 | $hostArr = explode(":", $db_host); 223 | $config = array( 224 | "provider" => 'wordpress', 225 | "database" => array( 226 | 'host' => $hostArr[0], 227 | 'port' => $hostArr[1], 228 | 'user' => $db_user, 229 | 'password' => $db_pass, 230 | 'database' => $db_name, 231 | 'charset' => 'utf8', 232 | 'prefix' => $prefix 233 | ), 234 | ); 235 | 236 | return $config; 237 | } 238 | 239 | private function writeConfig($config) 240 | { 241 | if (empty($config)) { 242 | showMessage("无法获取配置文件,接口无法正常工作"); 243 | } 244 | $configString = "configPath, $configString); 246 | if(!$result){ 247 | showMessage("无法写入配置,目录权限不足"); 248 | } 249 | } 250 | } 251 | 252 | class appAction 253 | { 254 | public $provider; 255 | public $pageConfig; 256 | public $data; 257 | 258 | public function __construct($config) 259 | { 260 | $this->provider = new provider($config); 261 | } 262 | 263 | public function handle() 264 | { 265 | $funcName = $_GET['a'] . "Action"; 266 | if (method_exists($this, $funcName)) { 267 | $this->_initTags(); 268 | $this->{$funcName}(); 269 | } else { 270 | if($_SERVER['HTTP_VERSION']){ 271 | res(-1, '错误的入口'); 272 | }else{ 273 | showMessage('接口访问正常'); 274 | } 275 | } 276 | } 277 | 278 | private function _initTags() 279 | { 280 | if (isset($_GET['tags'])) { 281 | $tags = json_decode($_GET['tags'], true); 282 | if (is_array($tags)) { 283 | foreach ($tags as $dataName => $item) { 284 | $this->data[$dataName] = $this->_addTagData($item['tag'], $item['args'], $item['children']); 285 | } 286 | } 287 | } 288 | } 289 | 290 | private function _addTagData($tag, $args, $children = null) 291 | { 292 | $tagName = "get" . ucfirst($tag); 293 | $data = null; 294 | if (method_exists($this->provider, $tagName)) { 295 | $data = $this->provider->$tagName($args); 296 | if (isset($data['data'])) { 297 | $isEmpty = empty($data['data']); 298 | } else { 299 | $isEmpty = empty($data); 300 | } 301 | if (!empty($children) && !$isEmpty) { 302 | $eachData = isset($data['data']) ? $data['data'] : $data; 303 | foreach ($eachData as $key => &$datum) { 304 | foreach ($children as $childName => $child) { 305 | $childArgs = $child['args']; 306 | foreach ($childArgs as $k => $v) { 307 | if (strpos($v, 'parent:') === 0) { 308 | $parentKey = substr($v, 7); 309 | $childArgs[$k] = $datum[$parentKey]; 310 | } 311 | } 312 | $datum[$childName] = $this->_addTagData($child['tag'], $childArgs, $child['children']); 313 | } 314 | } 315 | isset($data['data']) ? $data['data'] = $eachData : $data = $eachData; 316 | } 317 | } 318 | 319 | return $data; 320 | } 321 | 322 | /** 323 | * 首页 324 | */ 325 | public function indexAction() 326 | { 327 | $settings = $this->provider->getSetting(); 328 | $this->_pageConfig($settings); 329 | res(0, null, $this->data); 330 | } 331 | 332 | public function categoriesAction() 333 | { 334 | 335 | } 336 | 337 | public function categoryAction() 338 | { 339 | $id = intval($_GET['id']); 340 | $category = $this->provider->getCategory(array('id' => $id, 'child' => $_GET['child'])); 341 | $this->data['category'] = $category; 342 | $this->_pageConfig($category); 343 | res(0, null, $this->data); 344 | } 345 | 346 | public function listAction() 347 | { 348 | $categoryId = intval($_GET['categoryId']); 349 | $page = intval($_GET['page']); 350 | $row = intval($_GET['row']); 351 | if ($page < 1) { 352 | $page = 1; 353 | } 354 | if ($row < 1) { 355 | $row = 1; 356 | } 357 | $category = $this->provider->getCategory(array('id' => $categoryId)); 358 | if (!$category) { 359 | res(-1, '分类内容不存在'); 360 | } 361 | $args = array( 362 | 'categoryId' => $categoryId, 363 | 'page' => $page, 364 | 'row' => $row 365 | ); 366 | $args = array_merge($_GET, $args); 367 | if ($category['model'] == 'product') { 368 | $result = $this->provider->getProducts($args); 369 | } else { 370 | $result = $this->provider->getArticles($args); 371 | } 372 | $hasNext = $page * $row < $result['count'] ? true : false; 373 | res(0, null, $result['data'], array( 374 | 'count' => $result['count'], 375 | 'hasNext' => $hasNext, 376 | )); 377 | } 378 | 379 | public function postsAction() 380 | { 381 | res(0, null, $this->data); 382 | } 383 | 384 | public function articlesAction() 385 | { 386 | 387 | res(0, null, $this->data); 388 | } 389 | 390 | public function articleAction() 391 | { 392 | $id = intval($_GET['id']); 393 | $datum = $this->provider->getArticle(array('id' => $id)); 394 | $this->data['article'] = $datum; 395 | $this->_pageConfig($datum); 396 | res(0, null, $this->data); 397 | } 398 | 399 | public function productsAction() 400 | { 401 | $categoryId = intval($_GET['categoryId']); 402 | $page = intval($_GET['page']); 403 | res(0, null, $this->data); 404 | } 405 | 406 | public function productAction() 407 | { 408 | $id = intval($_GET['id']); 409 | $datum = $this->provider->getProduct(array('id' => $id)); 410 | if (!$datum) { 411 | res(-1, '产品不存在'); 412 | } 413 | $this->data['product'] = $datum; 414 | $this->_pageConfig($datum); 415 | res(0, null, $this->data); 416 | } 417 | 418 | public function defaultAction() 419 | { 420 | res(0, null, $this->data); 421 | } 422 | 423 | public function settingAction() 424 | { 425 | $settings = $this->provider->getSetting(); 426 | res(0, null, array( 427 | 'setting' => $settings 428 | )); 429 | } 430 | 431 | public function pageAction() 432 | { 433 | $page = $_GET['page']; 434 | $pageId = $_GET['pageId']; 435 | $datum = $this->provider->getPage(array('page' => $page, 'pageId' => $pageId)); 436 | $this->data['page'] = $datum; 437 | res(0, null, $this->data); 438 | } 439 | 440 | public function updateAction() 441 | { 442 | $version = $_SERVER['HTTP_VERSION']; 443 | $domain = $_SERVER['HTTP_HOST']; 444 | $url = 'https://www.mingze.vip/version/app'; 445 | $remoteVersion = $this->request($url, 'get', array('version' => $version, 'domain' => $domain)); 446 | $remoteVersion = json_decode($remoteVersion, true); 447 | 448 | if($remoteVersion == null){ 449 | res(0, null); 450 | } 451 | 452 | if($remoteVersion['code'] !== 0){ 453 | res($remoteVersion['code'], $remoteVersion['msg']); 454 | } 455 | 456 | res(0, '不需要更新'); 457 | } 458 | 459 | public function sitemapAction(){ 460 | $sitemap = $this->provider->getSitemap(); 461 | 462 | $content = implode("\r\n", $sitemap); 463 | header("Content-type:text/plan"); 464 | /*header("Content-type:application/octet-stream"); 465 | header("Accept-Ranges:bytes"); 466 | header("Accept-Length:".strlen($content)); 467 | header("Content-Disposition: attachment; filename=sitemap.txt");*/ 468 | 469 | echo $content; 470 | } 471 | 472 | public function mappingAction(){ 473 | $sitemap = $this->provider->getMapping(); 474 | 475 | header("Content-type:text/plan"); 476 | //$content = implode("\r\n", $sitemap); 477 | foreach($sitemap as $key => $item){ 478 | echo "$key => $item\r\n"; 479 | } 480 | /*header("Content-type:application/octet-stream"); 481 | header("Accept-Ranges:bytes"); 482 | header("Accept-Length:".strlen($content)); 483 | header("Content-Disposition: attachment; filename=sitemap.txt");*/ 484 | 485 | //echo $content; 486 | } 487 | 488 | private function _pageConfig($data) 489 | { 490 | if (!$data['seoTitle']) { 491 | $data['seoTitle'] = $data['title']; 492 | } 493 | $this->data['pageConfig'] = array( 494 | "seoTitle" => $data['seoTitle'], 495 | "keywords" => $data['keywords'], 496 | "description" => $data['description'], 497 | "title" => $data['title'] ? $data['title'] : $data['seoTitle'], 498 | "addTime" => $data['addTime'], 499 | "image" => $data['logo'], 500 | ); 501 | } 502 | 503 | private function request($url, $method = 'GET', $postFields = null, $time_out = 5) 504 | { 505 | $curl = curl_init(); 506 | curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 3); 507 | curl_setopt($curl, CURLOPT_TIMEOUT, $time_out); 508 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE); 509 | curl_setopt($curl, CURLOPT_HEADER, FALSE); 510 | 511 | if(is_array($postFields)){ 512 | $postFields = http_build_query($postFields); 513 | } 514 | 515 | $method = strtoupper($method); 516 | if($method == 'POST'){ 517 | curl_setopt($curl, CURLOPT_POST, TRUE); 518 | if ($postFields) { 519 | curl_setopt($curl, CURLOPT_POSTFIELDS, $postFields); 520 | } 521 | }else{ 522 | if($postFields){ 523 | if(strpos($url, '?') !== false){ 524 | $url .= "&" . $postFields; 525 | }else{ 526 | $url .= "?" . $postFields; 527 | } 528 | } 529 | } 530 | 531 | curl_setopt($curl, CURLOPT_URL, $url); 532 | curl_setopt($curl, CURLINFO_HEADER_OUT, TRUE); 533 | 534 | if (substr($url, 0, 8) == 'https://') { 535 | curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); 536 | curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE); 537 | } 538 | 539 | $response = curl_exec($curl); 540 | curl_close($curl); 541 | 542 | return $response; 543 | } 544 | } 545 | 546 | class provider 547 | { 548 | protected $categoryFields = array( 549 | 'id' => '', 550 | 'title' => '', 551 | 'content' => '', 552 | 'parentId' => '', 553 | 'seoTitle' => '', 554 | 'keywords' => '', 555 | 'description' => '', 556 | 'modelId' => '', 557 | 'typeId' => '', 558 | 'status' => '', 559 | 'sort' => '', 560 | 'logo' => '', 561 | 'views' => '', 562 | 'slug' => '', 563 | 'addTime' => '', 564 | ); 565 | protected $articleFields = array( 566 | 'id' => '', 567 | 'title' => '', 568 | 'content' => '', 569 | 'typeId' => '', 570 | 'parentId' => '', 571 | 'seoTitle' => '', 572 | 'keywords' => '', 573 | 'description' => '', 574 | 'status' => '', 575 | 'sort' => '', 576 | 'logo' => '', 577 | 'views' => '', 578 | 'categoryId' => '', 579 | 'slug' => '', 580 | 'addTime' => '', 581 | ); 582 | protected $productFields = array( 583 | 'id' => '', 584 | 'title' => '', 585 | 'content' => '', 586 | 'typeId' => '', 587 | 'parentId' => '', 588 | 'seoTitle' => '', 589 | 'keywords' => '', 590 | 'description' => '', 591 | 'status' => '', 592 | 'sort' => '', 593 | 'logo' => '', 594 | 'views' => '', 595 | 'categoryId' => '', 596 | 'slug' => '', 597 | 'money' => '', 598 | 'addTime' => '', 599 | ); 600 | /** 601 | * @var dedecmsProvider 602 | */ 603 | public $provider; 604 | public $config; 605 | 606 | public function __construct($config) 607 | { 608 | $providerName = $config['provider'] . 'Provider'; 609 | $this->provider = new $providerName($config); 610 | } 611 | 612 | public function initApp(){ 613 | $this->provider->initApp(); 614 | } 615 | 616 | public function getSetting() 617 | { 618 | return $this->provider->getSetting(); 619 | } 620 | 621 | public function getCategories($args = null) 622 | { 623 | return $this->provider->getCategories($args); 624 | } 625 | 626 | public function getCategory($args) 627 | { 628 | $category = $this->provider->getCategory($args); 629 | 630 | return $category; 631 | } 632 | 633 | public function getPosts($args = null) 634 | { 635 | return $this->provider->getPosts($args); 636 | } 637 | 638 | public function getArticles($args = null) 639 | { 640 | return $this->provider->getArticles($args); 641 | } 642 | 643 | public function getArticle($args) 644 | { 645 | $article = $this->provider->getArticle($args); 646 | 647 | return $article; 648 | } 649 | 650 | public function getProducts($args = null) 651 | { 652 | return $this->provider->getProducts($args); 653 | } 654 | 655 | public function getProduct($args) 656 | { 657 | $product = $this->provider->getProduct($args); 658 | 659 | return $product; 660 | } 661 | 662 | public function getPage($args) 663 | { 664 | $page = $this->provider->getPage($args); 665 | 666 | return $page; 667 | } 668 | 669 | public function getSitemap($args = null){ 670 | $sitemap = array( 671 | "pages/index/index", 672 | "pages/about/index", 673 | "pages/contact/index", 674 | "pages/products/index", 675 | "pages/articles/index" 676 | ); 677 | $categories = $this->provider->getCategories(); 678 | foreach ($categories as $item){ 679 | $sitemap[] = "pages/category/index?id=" . $item['id']; 680 | } 681 | $products = $this->provider->getProducts(array('result' => 'all')); 682 | foreach ($products['data'] as $item){ 683 | $sitemap[] = "pages/product/index?id=" . $item['id']; 684 | } 685 | $articles = $this->provider->getArticles(array('result' => 'all')); 686 | foreach ($articles['data'] as $item){ 687 | $sitemap[] = "pages/article/index?id=" . $item['id']; 688 | } 689 | 690 | return $sitemap; 691 | } 692 | 693 | public function getMapping($args = null){ 694 | $sitemap = $this->provider->getMapping($args); 695 | 696 | return $sitemap; 697 | } 698 | } 699 | 700 | class dedecmsProvider 701 | { 702 | protected $db; 703 | protected $settings; 704 | protected $productCategoryIds; 705 | 706 | public function __construct($config) 707 | { 708 | $this->db = new pdoMysql($config['database']); 709 | } 710 | 711 | public function initApp(){ 712 | $defaultSetting = array( 713 | array('varname' => 'app_mobile', 'value' => '', 'info' => '联系电话', 'groupid' => 1, 'type' => 'string'), 714 | array('varname' => 'app_logo', 'value' => 'http://www.dedecms.com/img/top_logo.jpg', 'info' => '小程序logo', 'groupid' => 1, 'type' => 'string'), 715 | array('varname' => 'app_company', 'value' => '', 'info' => '公司名称', 'groupid' => 1, 'type' => 'string'), 716 | array('varname' => 'app_address', 'value' => '', 'info' => '公司地址', 'groupid' => 1, 'type' => 'string'), 717 | array('varname' => 'app_about_id', 'value' => '', 'info' => '关于我们分类id', 'groupid' => 1, 'type' => 'string'), 718 | array('varname' => 'app_contact_id', 'value' => '', 'info' => '联系我们分类id', 'groupid' => 1, 'type' => 'string'), 719 | array('varname' => 'app_ignore_category', 'value' => '', 'info' => '需要排除的栏目,多个,隔开:', 'groupid' => 1, 'type' => 'string'), 720 | array('varname' => 'app_product_model_id', 'value' => '', 'info' => '产品模型id,默认是6:', 'groupid' => 1, 'type' => 'string'), 721 | array('varname' => 'app_product_category', 'value' => '', 'info' => '产品分类,只有在使用产品模型的时候才需要填写:', 'groupid' => 1, 'type' => 'string'), 722 | array('varname' => 'app_show_product', 'value' => '', 'info' => '是否显示产品,1显示,0不显示:', 'groupid' => 1, 'type' => 'string'), 723 | ); 724 | foreach ($defaultSetting as $value){ 725 | $exists = $this->db->getOne('varname', "sysconfig", "varname = '{$value['varname']}'"); 726 | if(!$exists){ 727 | $this->db->insert($value, "sysconfig", false, true); 728 | } 729 | } 730 | } 731 | 732 | public function getSetting() 733 | { 734 | $args = $_GET; 735 | if ($this->settings) { 736 | return $this->settings; 737 | } 738 | $result = $this->db->select('varname,value', "sysconfig"); 739 | $settings = array(); 740 | /** 741 | * 内部需要 742 | */ 743 | $settings['app_ignore_category'] = $args['ignoreCategory']; 744 | $settings['app_product_model_id'] = $args['productModelId']; 745 | $settings['app_product_category'] = $args['productCategory']; 746 | $settings['app_show_product'] = $args['showProduct']; 747 | 748 | foreach ($result as $value) { 749 | if(in_array($value['varname'], [ 750 | 'app_ignore_category', 751 | 'app_product_model_id', 752 | 'app_product_category', 753 | 'app_show_product' 754 | ]) && !$value['value']){ 755 | continue; 756 | } 757 | $settings[$value['varname']] = $value['value']; 758 | } 759 | $this->settings = array( 760 | 'seoTitle' => $settings['cfg_webname'], 761 | 'keywords' => $settings['cfg_keywords'], 762 | 'description' => $settings['cfg_description'], 763 | 'mobile' => $settings['app_mobile'], 764 | 'logo' => $settings['app_logo'], 765 | 'aboutId' => $settings['app_about_id'], 766 | 'contactId' => $settings['app_contact_id'], 767 | 'company' => $settings['app_company'], 768 | 'address' => $settings['app_address'], 769 | 'ignoreCategory' => $settings['app_ignore_category'], 770 | 'productModelId' => $settings['app_product_model_id'] ? $settings['app_product_model_id'] : 6, 771 | 'productCategory' => $settings['app_product_category'], 772 | 'showProduct' => $settings['app_show_product'], 773 | ); 774 | 775 | return $this->settings; 776 | } 777 | 778 | public function getCategories($args = null) 779 | { 780 | $settings = $this->getSetting(); 781 | $where = array("corank = 0 and ishidden = 0"); 782 | if($settings['ignoreCategory']){ 783 | //兼容处理 784 | $settings['ignoreCategory'] = str_replace(",", ",", $settings['ignoreCategory']); 785 | $where[] = "id NOT IN({$settings['ignoreCategory']})"; 786 | } 787 | //支持指定id 788 | if($args['categoryId']){ 789 | if (strpos($args['categoryId'], ',') !== false) { 790 | $where[] = "id IN({$args['categoryId']})"; 791 | } else { 792 | $where[] = 'id IN (' . implode(',', $this->_getSubCatIds($args['categoryId'])) . ')'; 793 | } 794 | }else if ($args['model']) { 795 | //检查 796 | $productCategoryIds = null; 797 | $productCategoryIds = $this->_getProductCategoryIds(); 798 | 799 | if($productCategoryIds){ 800 | if($args['model'] == 'product'){ 801 | $where[] = 'id IN (' . implode(',', $productCategoryIds) . ')'; 802 | }else{ 803 | $where[] = 'id NOT IN (' . implode(',', $productCategoryIds) . ')'; 804 | } 805 | }else{ 806 | $modelId = $this->_getModelId($args['model']); 807 | $where[] = "channeltype IN($modelId)"; 808 | } 809 | } 810 | if (isset($args['parentId'])) { 811 | $where[] = "reid = '{$args['parentId']}'"; 812 | } 813 | $categories = $this->db->select("id,reid as parentId,sortrank as sort,typename as title,channeltype as modelId,ispart as typeId,description,typedir,defaultname,namerule", "arctype", implode(' AND ', $where), $args['row'], "parentId ASC,sort ASC,id ASC"); 814 | foreach ($categories as $key => $category) { 815 | if ($args['child']) { 816 | $category['children'] = $this->db->select("id,reid as parentId,sortrank as sort,typename as title,channeltype as modelId,ispart as typeId,description", "arctype", "reid = '{$category['id']}", $args['row'], "sort ASC,id ASC"); 817 | foreach ($category['children'] as $key => $child) { 818 | //增加内容属性 819 | $child['model'] = $this->_getModel($child['modelId'], $child['id']); 820 | $category['children'][$key] = $child; 821 | } 822 | } 823 | $category['model'] = $this->_getModel($category['modelId'], $category['id']); 824 | $categories[$key] = $category; 825 | } 826 | usort($categories, function($item1, $item2){ 827 | return $item1['model'] < $item2['model']; 828 | }); 829 | 830 | return $categories; 831 | } 832 | 833 | public function getCategory($args) 834 | { 835 | $category = $this->db->getOne("id,reid as parentId,sortrank as sort,typename as title,channeltype as modelId,ispart as typeId,description,keywords,seotitle as seoTitle,content", 'arctype', "id = {$args['id']}"); 836 | if (!$category) { 837 | return null; 838 | } 839 | //增加内容属性 840 | $category['model'] = $this->_getModel($category['modelId'], $category['id']); 841 | if ($args['child']) { 842 | $category['children'] = $this->db->select("id,reid as parentId,sortrank as sort,typename as title,channeltype as modelId,ispart as typeId,description", "arctype", "reid = '{$category['id']}'", "", "sort ASC"); 843 | foreach ($category['children'] as $key => $child) { 844 | //增加内容属性 845 | $child['model'] = $this->_getModel($child['modelId'], $child['id']); 846 | $category['children'][$key] = $child; 847 | } 848 | } 849 | 850 | return $category; 851 | } 852 | 853 | public function getPosts($args = null) 854 | { 855 | return $this->_getArchives($args); 856 | } 857 | 858 | public function getArticles($args = null) 859 | { 860 | $args['modelId'] = "1,2"; 861 | 862 | return $this->_getArchives($args); 863 | } 864 | 865 | public function getArticle($args) 866 | { 867 | $args['modelId'] = "1,2"; 868 | 869 | return $this->_getArchive($args); 870 | } 871 | 872 | public function getProducts($args = null) 873 | { 874 | $args['modelId'] = 6; 875 | 876 | return $this->_getArchives($args); 877 | } 878 | 879 | public function getProduct($args) 880 | { 881 | $args['modelId'] = 6; 882 | 883 | return $this->_getArchive($args); 884 | } 885 | 886 | public function getPage($args) 887 | { 888 | $settings = $this->getSetting(); 889 | $pageId = 0; 890 | switch ($args['page']) { 891 | case 'about': 892 | $pageId = $settings['aboutId']; 893 | break; 894 | case 'contact': 895 | $pageId = $settings['contactId']; 896 | break; 897 | } 898 | if(!$pageId && $args['pageId']){ 899 | $pageId = $args['pageId']; 900 | } 901 | if (!$pageId) { 902 | return null; 903 | } 904 | //默认读取的是单页,如果不存在,则尝试读取分类 905 | $datum = $this->db->getOne("aid as id,title,filename,body as content,uptime as addTime", 'sgpage', "aid = '$pageId'"); 906 | 907 | if(!$datum) { 908 | $datum = $this->getCategory(array('id' => $pageId)); 909 | } 910 | return $datum; 911 | } 912 | 913 | public function getMapping($args = null){ 914 | $settings = $this->getSetting(); 915 | 916 | $categories = $this->getCategories(); 917 | $newCategories = array(); 918 | foreach ($categories as $item){ 919 | $newCategories[$item['id']] = $item; 920 | } 921 | unset($categories); 922 | 923 | $sitemap = array( 924 | BASE_URL => "pages/index/index", 925 | ); 926 | 927 | foreach ($newCategories as $item){ 928 | if(strpos($item['typedir'], 'http') === false) { 929 | $url = str_replace('{cmspath}/', BASE_URL, $item['typedir']) . "/" . $item["defaultname"]; 930 | }else{ 931 | $url = $item['typedir']; 932 | } 933 | if($item['id'] == $settings['aboutId']){ 934 | $appUrl = "pages/about/index"; 935 | }else if($item['id'] == $settings['contactId']){ 936 | $appUrl = "pages/contact/index"; 937 | }else{ 938 | $appUrl = "pages/category/index?id=" . $item['id']; 939 | } 940 | 941 | $sitemap[$url] = $appUrl; 942 | } 943 | 944 | $products = $this->getProducts(array('result' => 'all')); 945 | foreach ($products['data'] as $item){ 946 | $category = $newCategories[$item['categoryId']]; 947 | if(!$category){ 948 | continue; 949 | } 950 | $y = date('Y', $item['addTime']); 951 | $m = date('m', $item['addTime']); 952 | $d = date('d', $item['addTime']); 953 | $url = str_replace('{Y}', $y, $category['namerule']); 954 | $url = str_replace('{M}', $m, $url); 955 | $url = str_replace('{D}', $d, $url); 956 | $url = str_replace('{aid}', $item['id'], $url); 957 | $typedir = str_replace('{cmspath}/', BASE_URL, $category['typedir']); 958 | $url = str_replace('{typedir}', $typedir, $url); 959 | $sitemap[$url] = "pages/product/index?id=" . $item['id']; 960 | } 961 | $articles = $this->getArticles(array('result' => 'all')); 962 | foreach ($articles['data'] as $item){ 963 | $category = $newCategories[$item['categoryId']]; 964 | if(!$category){ 965 | continue; 966 | } 967 | $y = date('Y', $item['addTime']); 968 | $m = date('m', $item['addTime']); 969 | $d = date('d', $item['addTime']); 970 | $url = str_replace('{Y}', $y, $category['namerule']); 971 | $url = str_replace('{M}', $m, $url); 972 | $url = str_replace('{D}', $d, $url); 973 | $url = str_replace('{aid}', $item['id'], $url); 974 | $typedir = str_replace('{cmspath}/', BASE_URL, $category['typedir']); 975 | $url = str_replace('{typedir}', $typedir, $url); 976 | $sitemap[$url] = "pages/article/index?id=" . $item['id']; 977 | } 978 | 979 | return $sitemap; 980 | } 981 | 982 | private function _getArchives($args) 983 | { 984 | if (!$args['page']) { 985 | $args['page'] = 1; 986 | } 987 | if (!$args['row']) { 988 | $args['row'] = 10; 989 | } 990 | if($args['result'] == 'all') { 991 | $args['row'] = 100000; 992 | } 993 | if(!$args['order']){ 994 | $args['order'] = 'desc'; 995 | } 996 | $where = array("arcrank > -1"); 997 | 998 | if ($args['sudDay']) { 999 | $where[] = "senddate > " . strtotime("-{$args['sudDay']} day"); 1000 | } 1001 | if ($args['keyword']) { 1002 | $keyword = str_replace(',', '|', $args['keyword']); 1003 | $where[] = "CONCAT(title,keywords) REGEXP '$keyword'"; 1004 | } 1005 | if ($args['categoryId']) { 1006 | if (strpos($args['categoryId'], ',') !== false) { 1007 | $where[] = "typeid IN ({$args['categoryId']})"; 1008 | } else { 1009 | $where[] = 'typeid IN (' . implode(',', $this->_getSubCatIds($args['categoryId'])) . ')'; 1010 | } 1011 | } elseif ($args['modelId']) { 1012 | //产品 1013 | $productCategoryIds = null; 1014 | if($args['modelId'] == 6){ 1015 | $productCategoryIds = $this->_getProductCategoryIds(); 1016 | } 1017 | 1018 | if($productCategoryIds){ 1019 | $where[] = 'typeid IN (' . implode(',', $productCategoryIds) . ')'; 1020 | }else{ 1021 | $where[] = "channel IN({$args['modelId']})"; 1022 | } 1023 | } 1024 | if ($args['flag']) { 1025 | if (strpos(',', $args['flag']) === false) { 1026 | $where[] = "FIND_IN_SET('{$args['flag']}', flag)"; 1027 | } else { 1028 | $flags = explode(',', $args['flag']); 1029 | foreach ($flags as $flag) { 1030 | if (trim($flag) == '') continue; 1031 | $where[] = "FIND_IN_SET('$flag', flag)"; 1032 | } 1033 | } 1034 | } 1035 | if ($args["ids"]) { 1036 | $where = array("id in({$args['ids']})"); 1037 | } 1038 | 1039 | if ($args['orderby'] == 'hot' || $args['orderby'] == 'click') $order = "click {$args['order']}"; 1040 | else if ($args['orderby'] == 'sort' || $args['orderby'] == 'pubdate') $order = "sortrank {$args['order']}"; 1041 | else if ($args['orderby'] == 'id') $order = "id {$args['order']}"; 1042 | else if ($args['orderby'] == 'near') $order = "ABS(cast(id as signed) - " . $args['id'] . ")"; 1043 | else if ($args['orderby'] == 'last') $order = "lastpost {$args['order']}"; 1044 | else if ($args['orderby'] == 'rand') $order = "rand()"; 1045 | else $order = "sortrank {$args['order']}"; 1046 | $counter = $this->db->count(implode(' and ', $where), 'archives'); 1047 | if($args['id']){ 1048 | $where[] = "id != '{$args['id']}'"; 1049 | } 1050 | 1051 | $articles = array(); 1052 | if ($counter > 0) { 1053 | $limit = ($args['page'] - 1) * $args['row'] . "," . $args['row']; 1054 | $articles = $this->db->select("id, typeid as categoryId, channel as modelId, title, writer as author, litpic as logo, senddate as addTime, description, click as views", "archives", implode(' and ', $where), $limit, $order); 1055 | foreach ($articles as $key => $article) { 1056 | //增加内容属性 1057 | $article['model'] = $this->_getModel($article['modelId'], $article['categoryId']); 1058 | //处理默认缩略图等 1059 | if (isset($article['logo'])) { 1060 | if ($article['logo'] == '-' || $article['logo'] == '') { 1061 | $article['logo'] = ''; 1062 | } 1063 | if ($article['logo'] && strpos($article['logo'], 'http') === false) { 1064 | $article['logo'] = BASE_URL . ltrim($article['logo'], '/'); 1065 | } 1066 | } 1067 | $articles[$key] = $article; 1068 | } 1069 | } 1070 | 1071 | return array('count' => $counter, 'data' => $articles); 1072 | } 1073 | 1074 | private function _getArchive($args) 1075 | { 1076 | $settings = $this->getSetting(); 1077 | $args['id'] = intval($args['id']); 1078 | $article = $this->db->getOne("id, typeid as categoryId, channel as modelId, title, writer as author, litpic as logo, senddate as addTime, description, click as views", 'archives', "id='{$args['id']}'"); 1079 | 1080 | if (empty($article)) { 1081 | return null; 1082 | } 1083 | //增加内容属性 1084 | $article['model'] = $this->_getModel($article['modelId'], $article['categoryId']); 1085 | //获取addon表 1086 | $addonTable = $this->db->getOneCol("addtable", 'channeltype', "id = '{$article['modelId']}'"); 1087 | $addon = $this->db->getOne("*", $addonTable, "aid = '{$args['id']}'"); 1088 | $contentField = 'body'; 1089 | if(!isset($addon['body'])){ 1090 | if(isset($addon['message'])){ 1091 | $contentField = 'message'; 1092 | }elseif(isset($addon['content'])){ 1093 | $contentField = 'content'; 1094 | }else{ 1095 | $fields = array_keys($addon); 1096 | foreach ($fields as $field){ 1097 | if(strpos($field, 'body') !== false || strpos($field, 'message') !== false || strpos($field, 'content') !== false || strpos($field, 'detail') !== false){ 1098 | $contentField = $field; 1099 | break; 1100 | } 1101 | } 1102 | } 1103 | } 1104 | 1105 | $article['content'] = $addon[$contentField]; 1106 | $article['money'] = $addon['trueprice']; 1107 | $article['origin'] = $addon['price']; 1108 | //读取分类 1109 | $article['category'] = $this->getCategory(array("id" => $article['categoryId'])); 1110 | //完成附加表信息读取 1111 | //处理默认缩略图等 1112 | if (isset($article['logo'])) { 1113 | if ($article['logo'] == '-' || $article['logo'] == '') { 1114 | $article['logo'] = ''; 1115 | } 1116 | if ($article['logo'] && strpos($article['logo'], 'http') === false) { 1117 | $article['logo'] = BASE_URL . ltrim($article['logo'], '/'); 1118 | } 1119 | } 1120 | //内容路径替换 1121 | preg_match_all('/]*/i', $article['content'], $matches); 1122 | if ($matches[1]) { 1123 | foreach ($matches[1] as $k => $item) { 1124 | if (!preg_match("/^http/",$item)) { 1125 | $article['content'] = str_replace($item, BASE_URL . ltrim(str_replace('../', '/', $item), '/'), $article['content']); 1126 | } 1127 | } 1128 | } 1129 | preg_match_all('/ $item) { 1132 | if (!preg_match("/^http/",$item)) { 1133 | $article['content'] = str_replace($item, BASE_URL . ltrim(str_replace('../', '/', $item), '/'), $article['content']); 1134 | } 1135 | } 1136 | } 1137 | //上下篇 1138 | $article['next'] = $this->db->getOne("id, typeid as categoryId, channel as modelId, title, writer as author, senddate as addTime, description, click as views", "archives", "arcrank > -1 AND id > {$article['id']} AND typeid='{$article['categoryId']}'", "id ASC"); 1139 | $article['prev'] = $this->db->getOne("id, typeid as categoryId, channel as modelId, title, writer as author, senddate as addTime, description, click as views", "archives", "arcrank > -1 AND id < {$article['id']} AND typeid='{$article['categoryId']}'", "id DESC"); 1140 | 1141 | return $article; 1142 | } 1143 | 1144 | private function _getProductCategoryIds(){ 1145 | if($this->productCategoryIds){ 1146 | return $this->productCategoryIds; 1147 | } 1148 | $settings = $this->getSetting(); 1149 | if($settings['showProduct'] && $settings['productCategory']){ 1150 | $this->productCategoryIds = $this->_getSubCatIds($settings['productCategory']); 1151 | } 1152 | 1153 | return $this->productCategoryIds; 1154 | } 1155 | 1156 | private function _getModel($modelId, $categoryId) 1157 | { 1158 | $settings = $this->getSetting(); 1159 | $productModelId = $settings['productModelId']; 1160 | $productCategoryIds = $this->_getProductCategoryIds(); 1161 | if($productCategoryIds){ 1162 | if(in_array($categoryId, $productCategoryIds)){ 1163 | return 'product'; 1164 | } 1165 | } 1166 | 1167 | switch ($modelId) { 1168 | case $productModelId: 1169 | $model = 'product'; 1170 | break; 1171 | default: 1172 | $model = 'article'; 1173 | break; 1174 | } 1175 | 1176 | return $model; 1177 | } 1178 | 1179 | private function _getModelId($model) 1180 | { 1181 | $settings = $this->getSetting(); 1182 | $productModelId = $settings['productModelId']; 1183 | switch ($model) { 1184 | case 'product': 1185 | $modelId = $productModelId; 1186 | break; 1187 | default: 1188 | $modelId = "1,2"; 1189 | break; 1190 | } 1191 | 1192 | return $modelId; 1193 | } 1194 | 1195 | private function _getSubCatIds($categoryId, $topId = null) 1196 | { 1197 | $categories = $this->db->select("id, reid as parentId", "arctype"); 1198 | 1199 | //$categories = $this->db->select("id,reid as parentId", "arctype", "topid = '{$topId}'"); 1200 | $ids = array($categoryId => $categoryId); 1201 | if (!empty($categories)) { 1202 | foreach ($categories as $category) { 1203 | if (in_array($category['parentId'], $ids)) { 1204 | $ids[$category['id']] = $category['id']; 1205 | } 1206 | foreach ($categories as $v) { 1207 | if (in_array($v['parentId'], $ids)) { 1208 | $ids[$v['id']] = $v['id']; 1209 | } 1210 | } 1211 | foreach ($categories as $v) { 1212 | if (in_array($v['parentId'], $ids)) { 1213 | $ids[$v['id']] = $v['id']; 1214 | } 1215 | } 1216 | } 1217 | } 1218 | 1219 | return array_values($ids); 1220 | } 1221 | } 1222 | 1223 | class phpcmsProvider 1224 | { 1225 | //todo phpcms 1226 | } 1227 | 1228 | class wordpressProvider 1229 | { 1230 | //todo wordpress 1231 | } 1232 | 1233 | class empireProvider 1234 | { 1235 | //todo 帝国 1236 | } 1237 | 1238 | /** 1239 | * 数据库操作类 1240 | */ 1241 | class pdoMysql 1242 | { 1243 | private $config = null; 1244 | /** @var PDO */ 1245 | public $link = null; 1246 | /** @var PDOStatement|int */ 1247 | public $lastqueryid = null; 1248 | public $querycount = 0; 1249 | 1250 | public function __construct($config) 1251 | { 1252 | if (!$config['port']) { 1253 | $config['port'] = 3306;//默认端口 1254 | } 1255 | if (!$config['charset']) { 1256 | $config['charset'] = 'utf8'; 1257 | } 1258 | $this->config = $config; 1259 | $this->config['dsn'] = 'mysql:host=' . $config['host'] . ';port=' . $config['port'] . ';dbname=' . $config['database']; 1260 | $this->connect(); 1261 | } 1262 | 1263 | public function connect() 1264 | { 1265 | try { 1266 | $this->link = new PDO($this->config['dsn'], $this->config['user'], $this->config['password'], array( 1267 | PDO::ATTR_PERSISTENT => true, 1268 | PDO::ATTR_EMULATE_PREPARES => false, 1269 | PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, 1270 | PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8" 1271 | )); 1272 | } catch (Exception $e) { 1273 | res(-1, $e->getMessage()); 1274 | } 1275 | //重置sql_mode,防止datetime,group by 出错 1276 | $this->link->query("set sql_mode=''"); 1277 | 1278 | return $this->link; 1279 | } 1280 | 1281 | private function execute($sql) 1282 | { 1283 | if (!is_object($this->link)) { 1284 | $this->connect(); 1285 | } 1286 | $this->lastqueryid = $this->link->exec($sql); 1287 | $this->querycount++; 1288 | 1289 | return $this->lastqueryid; 1290 | } 1291 | 1292 | public function query($sql = null) 1293 | { 1294 | if (!is_object($this->link)) { 1295 | $this->connect(); 1296 | } 1297 | $this->lastqueryid = $this->link->query($sql) or $this->halt("", $sql); 1298 | $this->querycount++; 1299 | 1300 | return $this->lastqueryid; 1301 | } 1302 | 1303 | public function select($data, $table, $where = '', $limit = '', $order = '', $group = '', $key = '') 1304 | { 1305 | $where = $where == '' ? '' : ' WHERE ' . $where; 1306 | $order = $order == '' ? '' : ' ORDER BY ' . $order; 1307 | $group = $group == '' ? '' : ' GROUP BY ' . $group; 1308 | $limit = $limit == '' ? '' : ' LIMIT ' . $limit; 1309 | $field = explode(',', $data); 1310 | array_walk($field, array($this, 'addSpecialChar')); 1311 | $data = implode(',', $field); 1312 | 1313 | $sql = 'SELECT ' . $data . ' FROM `' . $this->config['database'] . '`.`' . $this->getTable($table) . '`' . $where . $group . $order . $limit; 1314 | 1315 | $this->query($sql); 1316 | if (!is_object($this->lastqueryid)) { 1317 | return $this->lastqueryid; 1318 | } 1319 | $datalist = $this->lastqueryid->fetchAll(); 1320 | if ($key) { 1321 | $datalist_new = array(); 1322 | foreach ($datalist as $i => $item) { 1323 | $datalist_new[$item[$key]] = $item; 1324 | } 1325 | $datalist = $datalist_new; 1326 | unset($datalist_new); 1327 | } 1328 | $this->freeResult(); 1329 | 1330 | return $datalist; 1331 | } 1332 | 1333 | public function getOne($data, $table, $where = '', $order = '', $group = '') 1334 | { 1335 | $where = $where == '' ? '' : ' WHERE ' . $where; 1336 | $order = $order == '' ? '' : ' ORDER BY ' . $order; 1337 | $group = $group == '' ? '' : ' GROUP BY ' . $group; 1338 | $limit = ' LIMIT 1'; 1339 | $field = explode(',', $data); 1340 | array_walk($field, array($this, 'addSpecialChar')); 1341 | $data = implode(',', $field); 1342 | $sql = 'SELECT ' . $data . ' FROM `' . $this->config['database'] . '`.`' . $this->getTable($table) . '`' . $where . $group . $order . $limit; 1343 | $this->query($sql); 1344 | $res = $this->lastqueryid->fetch(); 1345 | $this->freeResult(); 1346 | 1347 | return $res; 1348 | } 1349 | 1350 | public function getOneCol($data, $table, $where = '', $order = '', $group = '') 1351 | { 1352 | $where = $where == '' ? '' : ' WHERE ' . $where; 1353 | $order = $order == '' ? '' : ' ORDER BY ' . $order; 1354 | $group = $group == '' ? '' : ' GROUP BY ' . $group; 1355 | $limit = ' LIMIT 1'; 1356 | $field = explode(',', $data); 1357 | array_walk($field, array($this, 'addSpecialChar')); 1358 | $data = implode(',', $field); 1359 | $fieldname = str_replace('`', '', $data); 1360 | $sql = 'SELECT ' . $data . ' FROM `' . $this->config['database'] . '`.`' . $this->getTable($table) . '`' . $where . $group . $order . $limit; 1361 | $this->query($sql); 1362 | $res = $this->lastqueryid->fetch(); 1363 | $this->freeResult(); 1364 | 1365 | $result = isset($res[$fieldname]) ? $res[$fieldname] : false; 1366 | 1367 | return $result; 1368 | } 1369 | 1370 | public function insert($data, $table, $return_insertId = false, $replace = false) 1371 | { 1372 | if (!is_array($data) || $table == '' || count($data) == 0) { 1373 | return false; 1374 | } 1375 | $fielddata = array_keys($data); 1376 | $valuedata = array_values($data); 1377 | array_walk($fielddata, array($this, 'addSpecialChar')); 1378 | array_walk($valuedata, array($this, 'escapeString')); 1379 | $field = implode(',', $fielddata); 1380 | $value = implode(',', $valuedata); 1381 | $cmd = $replace ? 'REPLACE INTO' : 'INSERT INTO'; 1382 | $sql = $cmd . ' `' . $this->config['database'] . '`.`' . $this->getTable($table) . '`(' . $field . ') VALUES (' . $value . ')'; 1383 | $return = $this->execute($sql); 1384 | 1385 | return $return_insertId ? $this->insertId() : $return; 1386 | } 1387 | 1388 | /** 1389 | * update 不支持order by 1390 | * @param $data 1391 | * @param $table 1392 | * @param string $where 1393 | * @param string $order 1394 | * @param string $limit 1395 | * @return bool|int|PDOStatement 1396 | */ 1397 | public function update($data, $table, $where = '', $order = "", $limit = "") 1398 | { 1399 | if ($table == '' or $where == '') { 1400 | return false; 1401 | } 1402 | $where = ' WHERE ' . $where; 1403 | if (is_string($data) && $data != '') { 1404 | $field = $data; 1405 | } elseif (is_array($data) && count($data) > 0) { 1406 | $fields = array(); 1407 | foreach ($data as $k => $v) { 1408 | switch (substr($v, 0, 2)) { 1409 | case '+=': 1410 | $v = substr($v, 2); 1411 | if (is_numeric($v)) { 1412 | $fields[] = $this->addSpecialChar($k) . '=' . $this->addSpecialChar($k) . '+' . $this->escapeString($v, '', false); 1413 | } 1414 | break; 1415 | case '-=': 1416 | $v = substr($v, 2); 1417 | if (is_numeric($v)) { 1418 | $fields[] = $this->addSpecialChar($k) . '=' . $this->addSpecialChar($k) . '-' . $this->escapeString($v, '', false); 1419 | } 1420 | break; 1421 | default: 1422 | //对自增自减字段的特殊处理 1423 | if (preg_match('/^`[a-z_0-9]+`\s*[\+\-]\s*[0-9]+$/', $v)) { 1424 | $fields[] = $this->addSpecialChar($k) . '=' . $v; 1425 | } else { 1426 | $fields[] = $this->addSpecialChar($k) . '=' . $this->escapeString($v); 1427 | } 1428 | } 1429 | } 1430 | $field = implode(',', $fields); 1431 | } else { 1432 | return false; 1433 | } 1434 | $order = !empty($order) ? " ORDER BY " . $order : ""; 1435 | $limit = !empty($limit) ? " LIMIT " . $limit : ""; 1436 | $sql = 'UPDATE `' . $this->config['database'] . '`.`' . $this->getTable($table) . '` SET ' . $field . $where . $order . $limit; 1437 | 1438 | return $this->execute($sql); 1439 | } 1440 | 1441 | public function delete($table, $where) 1442 | { 1443 | if ($table == '' || $where == '') { 1444 | return false; 1445 | } 1446 | $where = ' WHERE ' . $where; 1447 | $sql = 'DELETE FROM `' . $this->config['database'] . '`.`' . $this->getTable($table) . '`' . $where; 1448 | 1449 | return $this->execute($sql); 1450 | } 1451 | 1452 | public function count($where = '', $table, $group = '') 1453 | { 1454 | $r = $this->getOne("COUNT(*) AS num", $table, $where, '', $group); 1455 | 1456 | return $r['num']; 1457 | } 1458 | 1459 | public function fetchAll($res = null) 1460 | { 1461 | $type = PDO::FETCH_ASSOC; 1462 | if ($res) { 1463 | $res_query = $res; 1464 | } else { 1465 | $res_query = $this->lastqueryid; 1466 | } 1467 | 1468 | return $res_query->fetchAll($type); 1469 | } 1470 | 1471 | public function affectedRows() 1472 | { 1473 | return is_numeric($this->lastqueryid) ? $this->lastqueryid : 0; 1474 | } 1475 | 1476 | public function getPrimary($table) 1477 | { 1478 | $this->query("SHOW COLUMNS FROM " . $this->getTable($table)); 1479 | while ($r = $this->lastqueryid->fetch()) { 1480 | if ($r['Key'] == 'PRI') break; 1481 | } 1482 | 1483 | return $r['Field']; 1484 | } 1485 | 1486 | public function getFields($table) 1487 | { 1488 | $fields = array(); 1489 | $this->query("SHOW COLUMNS FROM " . $this->getTable($table)); 1490 | while ($r = $this->lastqueryid->fetch()) { 1491 | $fields[$r['Field']] = $r['Type']; 1492 | } 1493 | 1494 | return $fields; 1495 | } 1496 | 1497 | public function checkFields($table, $array) 1498 | { 1499 | $fields = $this->getFields($table); 1500 | $nofields = array(); 1501 | foreach ($array as $v) { 1502 | if (!array_key_exists($v, $fields)) { 1503 | $nofields[] = $v; 1504 | } 1505 | } 1506 | 1507 | return $nofields; 1508 | } 1509 | 1510 | public function tableExists($table) 1511 | { 1512 | $tables = $this->listTables(); 1513 | 1514 | return in_array($table, $tables) ? 1 : 0; 1515 | } 1516 | 1517 | public function listTables() 1518 | { 1519 | $tables = array(); 1520 | $this->query("SHOW TABLES"); 1521 | while ($r = $this->lastqueryid->fetch()) { 1522 | $tables[] = $r['Tables_in_' . $this->config['database']]; 1523 | } 1524 | 1525 | return $tables; 1526 | } 1527 | 1528 | public function fieldExists($table, $field) 1529 | { 1530 | $fields = $this->getFields($table); 1531 | 1532 | return array_key_exists($field, $fields); 1533 | } 1534 | 1535 | public function getTable($table) 1536 | { 1537 | if (!$this->config['prefix']) { 1538 | return $table; 1539 | } 1540 | if (strpos($table, $this->config['prefix']) === false) { 1541 | return $this->config['prefix'] . $table; 1542 | } 1543 | 1544 | return $table; 1545 | } 1546 | 1547 | public function numRows($sql) 1548 | { 1549 | $this->query($sql); 1550 | 1551 | return $this->lastqueryid->rowCount(); 1552 | } 1553 | 1554 | public function num_fields($sql) 1555 | { 1556 | $this->query($sql); 1557 | 1558 | return $this->lastqueryid->columnCount(); 1559 | } 1560 | 1561 | public function result($sql, $row) 1562 | { 1563 | $this->query($sql); 1564 | 1565 | return $this->lastqueryid->fetchColumn($row); 1566 | } 1567 | 1568 | public function error() 1569 | { 1570 | return $this->link->errorInfo(); 1571 | } 1572 | 1573 | public function errno() 1574 | { 1575 | return intval($this->link->errorCode()); 1576 | } 1577 | 1578 | public function insertId() 1579 | { 1580 | return $this->link->lastInsertId(); 1581 | } 1582 | 1583 | public function freeResult() 1584 | { 1585 | if (is_object($this->lastqueryid)) { 1586 | $this->lastqueryid = null; 1587 | } 1588 | } 1589 | 1590 | public function close() 1591 | { 1592 | if (is_object($this->link)) { 1593 | unset($this->link); 1594 | } 1595 | } 1596 | 1597 | /** 1598 | * 对字段两边加反引号,以保证数据库安全 1599 | * @param $value 数组值 1600 | * @return mixed|string|数组值 1601 | */ 1602 | public function addSpecialChar(&$value) 1603 | { 1604 | if ('*' == $value || false !== strpos($value, '(') || false !== strpos($value, '.') || false !== strpos($value, '`') || false !== strpos(strtolower($value), 'as')) { 1605 | //不处理包含* 或者 使用了sql方法, 使用了别名。 1606 | } else { 1607 | $value = '`' . trim($value) . '`'; 1608 | } 1609 | if (preg_match("/\b(select|insert|update|delete)\b/i", $value)) { 1610 | $value = preg_replace("/\b(select|insert|update|delete)\b/i", '', $value); 1611 | } 1612 | 1613 | return $value; 1614 | } 1615 | 1616 | /** 1617 | * 对字段值两边加引号,以保证数据库安全 1618 | * @param $value 数组值 1619 | * @param string|数组key $key 数组key 1620 | * @param int $quotation 1621 | * @return string|数组值 1622 | */ 1623 | public function escapeString(&$value, $key = '', $quotation = 1) 1624 | { 1625 | if ($quotation) { 1626 | $q = '\''; 1627 | } else { 1628 | $q = ''; 1629 | } 1630 | $value = $q . $value . $q; 1631 | 1632 | return $value; 1633 | } 1634 | 1635 | public function halt($message = '', $sql = '') 1636 | { 1637 | res(-1, 'Errno :' . $sql . implode(' ', $this->link->errorInfo())); 1638 | } 1639 | 1640 | /** 1641 | * @param array|string $string 1642 | * @param string $from_encoding 1643 | * @param string $target_encoding 1644 | * @return false|string 1645 | */ 1646 | public function convertEncoding($string, $from_encoding = 'GBK', $target_encoding = 'UTF-8') 1647 | { 1648 | if(is_array($string)){ 1649 | foreach ($string as $key => $value){ 1650 | $string[$key] = $this->convertEncoding($value, $from_encoding, $target_encoding); 1651 | } 1652 | }else{ 1653 | if (function_exists('mb_convert_encoding')) 1654 | { 1655 | $string = mb_convert_encoding($string, str_replace('//IGNORE', '', strtoupper($target_encoding)), $from_encoding); 1656 | } 1657 | else 1658 | { 1659 | if (strtoupper($target_encoding) == 'GB2312' or strtoupper($target_encoding) == 'GBK') 1660 | { 1661 | $target_encoding .= '//IGNORE'; 1662 | } 1663 | 1664 | $string = iconv($from_encoding, $target_encoding, $string); 1665 | } 1666 | } 1667 | return $string; 1668 | } 1669 | } 1670 | 1671 | function baseUrl() 1672 | { 1673 | $isHttps = false; 1674 | if (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off') { 1675 | $isHttps = true; 1676 | } elseif (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') { 1677 | $isHttps = true; 1678 | } elseif (!empty($_SERVER['HTTP_FRONT_END_HTTPS']) && strtolower($_SERVER['HTTP_FRONT_END_HTTPS']) !== 'off') { 1679 | $isHttps = true; 1680 | } 1681 | 1682 | return ($isHttps ? "https" : "http") . "://" . $_SERVER["HTTP_HOST"] . "/"; 1683 | } 1684 | 1685 | /** 1686 | * json输出 1687 | * @param $code 1688 | * @param null $msg 1689 | * @param null $data 1690 | * @param null $extra 1691 | */ 1692 | function res($code, $msg = null, $data = null, $extra = null) 1693 | { 1694 | @header('Content-Type:application/json;charset=UTF-8'); 1695 | $output = array( 1696 | 'code' => $code, 1697 | 'msg' => $msg, 1698 | 'data' => $data 1699 | ); 1700 | if (is_array($extra)) { 1701 | foreach ($extra as $key => $val) { 1702 | $output[$key] = $val; 1703 | } 1704 | } 1705 | echo json_encode($output); 1706 | die; 1707 | } 1708 | 1709 | function showMessage($msg){ 1710 | @header('Content-Type:text/html;charset=UTF-8'); 1711 | echo "
$msg
"; 1712 | die; 1713 | } 1714 | 1715 | //错误提示: 1716 | $errorCodes = array( 1717 | 0 => '正常', 1718 | -1 => '普通错误', 1719 | 101 => '没有授权', 1720 | 102 => '版本过旧', 1721 | 1001 => '访问受限', 1722 | 1002 => '没有配置或配置错误', 1723 | 1003 => '空间不支持', 1724 | ); 1725 | 1726 | ?> -------------------------------------------------------------------------------- /demo/001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/demo/001.png -------------------------------------------------------------------------------- /demo/002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/demo/002.png -------------------------------------------------------------------------------- /demo/003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/demo/003.png -------------------------------------------------------------------------------- /demo/004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/demo/004.png -------------------------------------------------------------------------------- /demo/005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/demo/005.png -------------------------------------------------------------------------------- /demo/006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/demo/006.png -------------------------------------------------------------------------------- /demo/007.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/demo/007.png -------------------------------------------------------------------------------- /demo/008.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/demo/008.png -------------------------------------------------------------------------------- /demo/009.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/demo/009.png -------------------------------------------------------------------------------- /demo/010.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/demo/010.png -------------------------------------------------------------------------------- /demo/011.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/demo/011.png -------------------------------------------------------------------------------- /demo/012.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/demo/012.png -------------------------------------------------------------------------------- /demo/013.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/demo/013.png -------------------------------------------------------------------------------- /demo/014.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/demo/014.png -------------------------------------------------------------------------------- /global.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.png'; 2 | declare module '*.gif'; 3 | declare module '*.jpg'; 4 | declare module '*.jpeg'; 5 | declare module '*.svg'; 6 | declare module '*.css'; 7 | declare module '*.less'; 8 | declare module '*.scss'; 9 | declare module '*.sass'; 10 | declare module '*.styl'; 11 | 12 | declare namespace NodeJS { 13 | interface ProcessEnv { 14 | TARO_ENV: 'weapp' | 'swan' | 'alipay' | 'h5' | 'rn' | 'tt' | 'quickapp' | 'qq' | 'jd' 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | const defineJestConfig = require('@tarojs/test-utils-react/dist/jest.js').default 2 | 3 | module.exports = defineJestConfig({ 4 | testEnvironment: 'jsdom', 5 | testMatch: ['/__tests__/?(*.)+(spec|test).[jt]s?(x)'] 6 | }) 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dedecms", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "dedecms-mini-program", 6 | "templateInfo": { 7 | "name": "taro-ui", 8 | "typescript": true, 9 | "css": "sass" 10 | }, 11 | "scripts": { 12 | "build:weapp": "taro build --type weapp", 13 | "build:swan": "taro build --type swan", 14 | "build:alipay": "taro build --type alipay", 15 | "build:tt": "taro build --type tt", 16 | "build:h5": "taro build --type h5", 17 | "build:rn": "taro build --type rn", 18 | "build:qq": "taro build --type qq", 19 | "build:jd": "taro build --type jd", 20 | "build:quickapp": "taro build --type quickapp", 21 | "dev:weapp": "npm run build:weapp -- --watch", 22 | "dev:swan": "npm run build:swan -- --watch", 23 | "dev:alipay": "npm run build:alipay -- --watch", 24 | "dev:tt": "npm run build:tt -- --watch", 25 | "dev:h5": "npm run build:h5 -- --watch", 26 | "dev:rn": "npm run build:rn -- --watch", 27 | "dev:qq": "npm run build:qq -- --watch", 28 | "dev:jd": "npm run build:jd -- --watch", 29 | "dev:quickapp": "npm run build:quickapp -- --watch", 30 | "test": "jest" 31 | }, 32 | "browserslist": [ 33 | "last 3 versions", 34 | "Android >= 4.1", 35 | "ios >= 8" 36 | ], 37 | "author": "", 38 | "dependencies": { 39 | "@babel/runtime": "^7.21.5", 40 | "@tarojs/components": "3.6.13", 41 | "@tarojs/helper": "3.6.13", 42 | "@tarojs/mini-runner": "^3.6.13", 43 | "@tarojs/plugin-framework-react": "3.6.13", 44 | "@tarojs/plugin-platform-alipay": "3.6.13", 45 | "@tarojs/plugin-platform-h5": "3.6.13", 46 | "@tarojs/plugin-platform-jd": "3.6.13", 47 | "@tarojs/plugin-platform-qq": "3.6.13", 48 | "@tarojs/plugin-platform-swan": "3.6.13", 49 | "@tarojs/plugin-platform-tt": "3.6.13", 50 | "@tarojs/plugin-platform-weapp": "3.6.13", 51 | "@tarojs/react": "3.6.13", 52 | "@tarojs/runtime": "3.6.13", 53 | "@tarojs/shared": "3.6.13", 54 | "@tarojs/taro": "3.6.13", 55 | "react": "^18.0.0", 56 | "react-dom": "^18.0.0", 57 | "taro-ui": "^v3.1.0-beta.4", 58 | "xss": "^1.0.6" 59 | }, 60 | "devDependencies": { 61 | "@babel/core": "^7.8.0", 62 | "@pmmmwh/react-refresh-webpack-plugin": "^0.5.5", 63 | "@tarojs/cli": "3.6.13", 64 | "@tarojs/taro-loader": "3.6.13", 65 | "@tarojs/test-utils-react": "^0.1.1", 66 | "@tarojs/webpack5-runner": "3.6.13", 67 | "@types/jest": "^29.3.1", 68 | "@types/node": "^18.15.11", 69 | "@types/react": "^18.0.0", 70 | "@types/webpack-env": "^1.13.6", 71 | "@typescript-eslint/eslint-plugin": "^6.2.0", 72 | "@typescript-eslint/parser": "^6.2.0", 73 | "babel-preset-taro": "3.6.13", 74 | "eslint": "^8.12.0", 75 | "eslint-config-taro": "3.6.13", 76 | "eslint-plugin-import": "^2.12.0", 77 | "eslint-plugin-react": "^7.8.2", 78 | "eslint-plugin-react-hooks": "^4.2.0", 79 | "jest": "^29.3.1", 80 | "jest-environment-jsdom": "^29.5.0", 81 | "postcss": "^8.4.18", 82 | "react-refresh": "^0.11.0", 83 | "stylelint": "^14.4.0", 84 | "ts-node": "^10.9.1", 85 | "tsconfig-paths-webpack-plugin": "^4.0.1", 86 | "typescript": "^5.1.0", 87 | "webpack": "5.78.0" 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /project.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "miniprogramRoot": "./dist", 3 | "projectname": "dedecms", 4 | "description": "dedecms-mini-program", 5 | "appid": "touristappid", 6 | "setting": { 7 | "urlCheck": true, 8 | "es6": false, 9 | "postcss": false, 10 | "minified": false 11 | }, 12 | "compileType": "miniprogram" 13 | } 14 | -------------------------------------------------------------------------------- /src/api.ts: -------------------------------------------------------------------------------- 1 | import fetch from './request' 2 | 3 | const api = { 4 | //对应接口 5 | index: ((params = {}) => { 6 | return fetch("?a=index", params, true); 7 | }), 8 | categories: ((params = {}) => { 9 | return fetch("categories", params); 10 | }), 11 | category: (params => { 12 | return fetch("?a=category", params, true); 13 | }), 14 | articles: ((params = {}) => { 15 | return fetch("?a=articles", params); 16 | }), 17 | article: (params => { 18 | return fetch("?a=article", params); 19 | }), 20 | products: ((params = {}) => { 21 | return fetch("?a=products", params); 22 | }), 23 | product: (params => { 24 | return fetch("?a=product", params); 25 | }), 26 | list: (params => { 27 | return fetch("?a=list", params); 28 | }), 29 | setting: ((params = {}) => { 30 | return fetch("?a=setting", params, true); 31 | }), 32 | page: (params => { 33 | return fetch("?a=page", params, true); 34 | }), 35 | default: (params => { 36 | return fetch("?a=default", params); 37 | }), 38 | update: ((params = {}) => { 39 | return fetch("?a=update", params, true); 40 | }), 41 | } 42 | 43 | export default api -------------------------------------------------------------------------------- /src/app.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | pages: [ 3 | 'pages/index/index', 4 | 'pages/category/index', 5 | 'pages/articles/index', 6 | 'pages/article/index', 7 | 'pages/products/index', 8 | 'pages/product/index', 9 | 'pages/about/index', 10 | 'pages/contact/index', 11 | 'pages/browser/index', 12 | ], 13 | window: { 14 | backgroundTextStyle: 'light', 15 | navigationBarBackgroundColor: '#3998fc', 16 | defaultTitle: "标题", 17 | titleBarColor: "#3998fc", 18 | gestureBack: "YES", 19 | navigationBarTitleText: "标题", 20 | navigationBarTextStyle: 'white' 21 | }, 22 | tabBar: { 23 | color: "#666", 24 | selectedColor: "#f44", 25 | backgroundColor: "#fafafa", 26 | borderStyle: 'black', 27 | list: [{ 28 | pagePath: "pages/index/index", 29 | iconPath: "./assets/home.png", 30 | selectedIconPath: "./assets/home-selected.png", 31 | text: "首页" 32 | }, 33 | { 34 | pagePath: "pages/products/index", 35 | iconPath: "./assets/shop.png", 36 | selectedIconPath: "./assets/shop-selected.png", 37 | text: "产品中心" 38 | }, 39 | { 40 | pagePath: "pages/articles/index", 41 | iconPath: "./assets/article.png", 42 | selectedIconPath: "./assets/article-selected.png", 43 | text: "文章中心" 44 | }, 45 | { 46 | pagePath: "pages/about/index", 47 | iconPath: "./assets/about.png", 48 | selectedIconPath: "./assets/about-selected.png", 49 | text: "关于我们" 50 | }, 51 | { 52 | pagePath: "pages/contact/index", 53 | iconPath: "./assets/contact.png", 54 | selectedIconPath: "./assets/contact-selected.png", 55 | text: "联系我们" 56 | }] 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/app.css: -------------------------------------------------------------------------------- 1 | .network-theme .panel-title{text-align:center;padding-left:20px;border-bottom:none;color:#266aae}.network-theme .panel-title .title-text{background:#fff;display:inline-block;padding:0 30rpx;position:relative;z-index:1}.network-theme .panel-title::before{content:" ";display:inline-block;position:absolute;height:0px;border-bottom:4px dashed #266aae;background-color:rgba(0,0,0,0);margin-top:-2px;width:60%;top:50%;left:20%;right:20%;box-shadow:none;border-radius:0}page{font-size:28px;box-sizing:border-box;color:#333}swan-template{display:contents}.container{background:#f6f6f6}.link{color:#266aae}.text-center{text-align:center}.panel{margin-top:20px}.panel-title{position:relative;padding:20px 40px;padding-left:40px;background:#fff;font-size:32px;font-weight:700;line-height:1.5;border-bottom:2px solid rgba(38,106,174,.1)}.panel-title::before{content:" ";display:inline-block;position:absolute;left:20px;top:50%;margin-top:-15px;width:6px;height:32px;background-color:#266aae;box-shadow:0 2px 4px 0 rgba(38,106,174,.2);border-radius:2px}.panel-content{padding:0 20px}.panel-content.no-padding{padding:0}.article{word-break:break-all;word-wrap:break-word;line-height:1.7;box-sizing:border-box}.article .article-header{margin-bottom:20px;box-shadow:1px 0 6px rgba(38,106,174,.2);background:#fff}.article .article-header .article-title{font-size:36px;padding:30px 20px 20px}.article .article-header .article-meta{color:#999;font-size:24px;padding:20px 0;margin:0 20px;border-top:2px solid rgba(38,106,174,.1)}.article .article-header .article-meta .article-meta-item{margin-right:20px}.article .article-header .article-meta .article-meta-item:last-child{margin-right:0}.article .article-logo{width:750px;vertical-align:bottom}.article .article-content{padding:30px 20px;background:#fff}.article .article-footer{background:#fff;position:relative;padding:20px;border-bottom:2px solid rgba(38,106,174,.1)}.article .article-footer .footer-item{line-height:56px}.article .article-footer::before{content:"";position:absolute;left:20px;right:20px;top:0;height:1;border-top:2px solid rgba(38,106,174,.1)}.divider{background:#fff}.tabs{border-bottom:2px solid rgba(38,106,174,.1)} -------------------------------------------------------------------------------- /src/app.scss: -------------------------------------------------------------------------------- 1 | @import 'var.scss'; 2 | @import 'theme.scss'; 3 | page{ 4 | font-size: 28px; 5 | box-sizing: border-box; 6 | color: #333; 7 | } 8 | swan-template { 9 | display: contents; 10 | } 11 | .container{ 12 | background: $background; 13 | } 14 | .link{ 15 | color: $primary; 16 | } 17 | .text-center{ 18 | text-align: center; 19 | } 20 | .panel{ 21 | margin-top: 20px; 22 | } 23 | 24 | .panel-title{ 25 | position: relative; 26 | padding:20px 40px; 27 | padding-left: 40px; 28 | background: #fff; 29 | font-size: 32px; 30 | font-weight: 700; 31 | line-height: 1.5; 32 | border-bottom: $border; 33 | &::before{ 34 | content: " "; 35 | display: inline-block; 36 | position: absolute; 37 | left: 20px; 38 | top: 50%; 39 | margin-top: -15px; 40 | width: 6px; 41 | height: 32px; 42 | background-color: $primary; 43 | box-shadow: 0 2px 4px 0 rgba($primary, 0.2); 44 | border-radius: 2px; 45 | } 46 | } 47 | 48 | .panel-content{ 49 | padding: 0 20px; 50 | } 51 | .panel-content.no-padding{ 52 | padding: 0; 53 | } 54 | .article{ 55 | word-break:break-all; 56 | word-wrap:break-word; 57 | line-height:1.7; 58 | box-sizing: border-box; 59 | .article-header{ 60 | margin-bottom: 20px; 61 | box-shadow: $shadow; 62 | background: #fff; 63 | .article-title{ 64 | font-size: 36px; 65 | padding: 30px 20px 20px; 66 | } 67 | .article-meta{ 68 | color: #999; 69 | font-size: 24px; 70 | padding: 20px 0; 71 | margin: 0 20px; 72 | border-top: $border; 73 | .article-meta-item{ 74 | margin-right: 20px; 75 | &:last-child{ 76 | margin-right: 0; 77 | } 78 | } 79 | } 80 | } 81 | .article-logo{ 82 | width: 750px; 83 | vertical-align: bottom; 84 | } 85 | .article-content{ 86 | padding: 30px 20px; 87 | background: #fff; 88 | } 89 | .article-footer{ 90 | background: #fff; 91 | position: relative; 92 | padding: 20px; 93 | border-bottom: $border; 94 | .footer-item{ 95 | line-height: 56px; 96 | } 97 | &::before{ 98 | content: ""; 99 | position:absolute; 100 | left: 20px; 101 | right: 20px; 102 | top: 0; 103 | height:1; 104 | border-top: $border; 105 | } 106 | } 107 | } 108 | .divider{ 109 | background: #fff; 110 | } 111 | .tabs{ 112 | border-bottom: $border; 113 | } 114 | -------------------------------------------------------------------------------- /src/app.ts: -------------------------------------------------------------------------------- 1 | import { Component } from 'react' 2 | import 'taro-ui/dist/style/components/activity-indicator.scss' 3 | import 'taro-ui/dist/style/components/button.scss' 4 | import 'taro-ui/dist/style/components/divider.scss' 5 | import 'taro-ui/dist/style/components/flex.scss' 6 | import 'taro-ui/dist/style/components/load-more.scss' 7 | import 'taro-ui/dist/style/components/loading.scss' 8 | import 'taro-ui/dist/style/components/tabs.scss' 9 | 10 | import './app.scss' 11 | 12 | class App extends Component { 13 | 14 | // this.props.children 是将要会渲染的页面 15 | render () { 16 | return this.props.children 17 | } 18 | } 19 | 20 | export default App 21 | -------------------------------------------------------------------------------- /src/assets/about-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/src/assets/about-selected.png -------------------------------------------------------------------------------- /src/assets/about.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/src/assets/about.png -------------------------------------------------------------------------------- /src/assets/article-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/src/assets/article-selected.png -------------------------------------------------------------------------------- /src/assets/article.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/src/assets/article.png -------------------------------------------------------------------------------- /src/assets/contact-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/src/assets/contact-selected.png -------------------------------------------------------------------------------- /src/assets/contact.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/src/assets/contact.png -------------------------------------------------------------------------------- /src/assets/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/src/assets/default.png -------------------------------------------------------------------------------- /src/assets/home-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/src/assets/home-selected.png -------------------------------------------------------------------------------- /src/assets/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/src/assets/home.png -------------------------------------------------------------------------------- /src/assets/nav-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/src/assets/nav-1.png -------------------------------------------------------------------------------- /src/assets/nav-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/src/assets/nav-2.png -------------------------------------------------------------------------------- /src/assets/nav-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/src/assets/nav-3.png -------------------------------------------------------------------------------- /src/assets/nav-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/src/assets/nav-4.png -------------------------------------------------------------------------------- /src/assets/phone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/src/assets/phone.png -------------------------------------------------------------------------------- /src/assets/shop-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/src/assets/shop-selected.png -------------------------------------------------------------------------------- /src/assets/shop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/src/assets/shop.png -------------------------------------------------------------------------------- /src/components/Banner/index.scss: -------------------------------------------------------------------------------- 1 | @import '../../var.scss'; 2 | .swiper{ 3 | height: 400px; 4 | box-shadow: $shadow; 5 | .swiper-item{ 6 | position: relative; 7 | .swiper-image{ 8 | width: 100%; 9 | height: 400px; 10 | vertical-align: bottom; 11 | } 12 | .swiper-text{ 13 | position: absolute; 14 | font-size: 24px; 15 | padding: 12px 20px; 16 | bottom: 0; 17 | left: 0; 18 | right: 0; 19 | color: #fff; 20 | background: rgba($primary, 0.3); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/components/Banner/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Taro from '@tarojs/taro' 3 | import { View, Image, Swiper, SwiperItem } from '@tarojs/components' 4 | import './index.scss' 5 | 6 | export default class Banner extends Component { 7 | static defaultProps = { 8 | list: [], 9 | } 10 | 11 | static options = { 12 | addGlobalClass: true, 13 | } 14 | 15 | gotoPosts = (e, m) => { 16 | if(!e){ 17 | return 18 | } 19 | if(m == 'product'){ 20 | this.gotoProduct(e) 21 | }else{ 22 | this.gotoArticle(e) 23 | } 24 | } 25 | 26 | gotoProduct = (e) => { 27 | Taro.navigateTo({ 28 | url: '/pages/product/index?id=' + e 29 | }) 30 | } 31 | 32 | gotoArticle = (e) => { 33 | Taro.navigateTo({ 34 | url: '/pages/article/index?id=' + e 35 | }) 36 | } 37 | 38 | render() { 39 | const { list } = this.props 40 | return ( 41 | 42 | {list.length > 0 && 47 | {list.map((item, index) => { 48 | return 49 | 50 | 51 | {item.title && {item.title}} 52 | 53 | 54 | })} 55 | } 56 | 57 | ) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/components/article/index.scss: -------------------------------------------------------------------------------- 1 | @import '../../var.scss'; 2 | .article-list{ 3 | .article-item{ 4 | display: flex; 5 | background: #fff; 6 | margin: 20px 0; 7 | padding: 20px; 8 | &:last-child{ 9 | border-bottom: none; 10 | } 11 | .item-thumb{ 12 | flex-shrink: 0; 13 | width: 216px; 14 | height: 148px; 15 | margin-left: 20px; 16 | .item-thumb-info{ 17 | width: 100%; 18 | height: 100%; 19 | } 20 | } 21 | .item-content{ 22 | flex: 1; 23 | .item-content-title{ 24 | margin-top: 10px; 25 | margin-bottom: 16px; 26 | display: -webkit-box; 27 | -webkit-box-orient: vertical; 28 | -webkit-line-clamp: 1; 29 | overflow: hidden; 30 | } 31 | .item-content-desc{ 32 | font-size: 24px; 33 | color: #999; 34 | display: -webkit-box; 35 | -webkit-box-orient: vertical; 36 | -webkit-line-clamp: 2; 37 | overflow: hidden; 38 | word-break:break-all; 39 | } 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/components/article/index.tsx: -------------------------------------------------------------------------------- 1 | import { Component } from 'react' 2 | import { Block, View, Image } from '@tarojs/components' 3 | import { AtLoadMore } from 'taro-ui' 4 | import Empty from '../empty' 5 | import './index.scss' 6 | import utils from '@/utils' 7 | 8 | export default class Article extends Component { 9 | static defaultProps = { 10 | articles: [], 11 | loading: !1, 12 | } 13 | 14 | static options = { 15 | addGlobalClass: true, 16 | } 17 | 18 | gotoArticle = (e) => { 19 | utils.navigate({ 20 | url: '/pages/article/index?id=' + e 21 | }) 22 | } 23 | 24 | render() { 25 | const { articles, loading } = this.props 26 | 27 | return ( 28 | 29 | {articles.length > 0 && 30 | {articles.map((item, index) => { 31 | return 32 | 33 | {item.title} 34 | {item.description && {item.description}} 35 | 36 | {item.logo && 37 | 38 | } 39 | 40 | })} 41 | } 42 | {loading && } 43 | {(!articles.length && !loading) && } 44 | 45 | ) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/components/cell/index.scss: -------------------------------------------------------------------------------- 1 | @import '../../var.scss'; 2 | .cell{ 3 | padding:24px 24px; 4 | position:relative; 5 | -webkit-box-sizing:border-box; 6 | box-sizing:border-box; 7 | background: #fff; 8 | color:#333; 9 | line-height:1.5; 10 | -webkit-transition:background-color 0.3s; 11 | -o-transition:background-color 0.3s; 12 | transition:background-color 0.3s; 13 | position:relative; 14 | display: flex; 15 | overflow:hidden; 16 | align-items:center; 17 | border-bottom: $border; 18 | .cell-title{ 19 | margin-right:40px; 20 | width: 200px; 21 | } 22 | .cell-value{ 23 | text-align: right; 24 | color: #666; 25 | flex: 1; 26 | } 27 | } -------------------------------------------------------------------------------- /src/components/cell/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { View } from '@tarojs/components' 3 | import './index.scss' 4 | 5 | export default class Empty extends Component { 6 | static defaultProps = { 7 | title: '', 8 | value: '', 9 | onClick: () => {} 10 | } 11 | 12 | static options = { 13 | addGlobalClass: true, 14 | } 15 | 16 | render() { 17 | const { title, value } = this.props 18 | 19 | return ( 20 | 21 | {title} 22 | {value} 23 | 24 | ) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/components/container/index.scss: -------------------------------------------------------------------------------- 1 | @import '../../var.scss'; 2 | .container{ 3 | background-color: $background; 4 | } -------------------------------------------------------------------------------- /src/components/container/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Taro from '@tarojs/taro' 3 | import { View, Text } from '@tarojs/components' 4 | import Header from '../header' 5 | import Footer from '../footer' 6 | import appConfig from '../../config' 7 | import Api from '../../api' 8 | import './index.scss' 9 | 10 | export default class Container extends Component { 11 | static defaultProps = { 12 | showHeader: false, 13 | showFooter: false, 14 | } 15 | 16 | static options = { 17 | addGlobalClass: true, 18 | } 19 | 20 | state = { 21 | setting: { 22 | mobile: appConfig.mobile, 23 | company: appConfig.company, 24 | address: appConfig.address, 25 | } 26 | } 27 | 28 | componentDidMount() { 29 | let { setting } = this.state 30 | Api.setting().then(res => { 31 | this.setState({ 32 | setting: Object.assign(setting, res.data.setting) 33 | }) 34 | }).catch(err => { 35 | Taro.showToast({ 36 | icon: 'none', 37 | title: err.msg || err.message 38 | }) 39 | }) 40 | Api.update().then(res => { 41 | }).catch(err => { 42 | if(err.code == 101){ 43 | Taro.showModal({ 44 | title: '授权提示', 45 | showCancel: !1, 46 | content: err.msg || err.message, 47 | }) 48 | }else if(err.code == 102){ 49 | Taro.showModal({ 50 | title: '新版提示', 51 | showCancel: !1, 52 | content: err.msg || err.message, 53 | }) 54 | } 55 | }) 56 | } 57 | 58 | render() { 59 | const { showHeader, showFooter, children } = this.props 60 | const { setting } = this.state 61 | 62 | return ( 63 | 64 | {this.props.showHeader &&
} 65 | {children} 66 | {this.props.showFooter &&