├── .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 | 
33 |
34 | 2. 首页TDK设置
35 | 首页TDK是指标题、关键词、描述,同网站首页的TDK一致,是小程序重要指标,务必认真填写。如图
36 | 
37 |
38 | 3. 联系方式设置
39 | 联系方式包括公司名称、公司地址、联系电话、联系微信,它们会显示在联系我们的页面里,以及各个页面的悬浮按钮。如图
40 | 
41 |
42 | 4. 内容设置
43 | 内容设置可以设置关于我们、联系我们、首页显示的分类内容、启用产品功能、停用产品功能等。
44 | 关于我们、联系我们需要指定单页id或分类id,优先使用单页id,只有填写了正确的id后,小程序才能正确调用关于我们和联系我们。如图
45 | 
46 | 小程序首页默认会显示一组分离以及分类的内容列表,正确配置首页显示的分类,可以让首页更美观以及将更有用的信息展示给用户。比如需要将培训视频、学员作品、客户案例展示在首页,他们的分类id分别是1,7,8,那么首页分类则填写1,7,8,它们之间需要使用英文的,隔开。如图
47 | 
48 |
49 | 6. 启用产品功能
50 | 将配置中的showProduct值设置为1,如果织梦站点使用商品模块来管理产品,则productCategory不需要设置,小程序会自动调用商品模块来获取产品分类和产品详情。如图
51 | 
52 |
53 | 如果织梦站点使用文章模块来管理产品,则还需要设置productCategory的值为产品分类的顶级分类id。如图
54 | 
55 | 同时还需要将产品中心的导航添加上。打开app.json,找到首页,在它下方加入产品中心,如图
56 | 
57 | 还需要将首页导航图标反注释。打开common.js,格式化它。如图
58 | 
59 |
60 | 7. 停用产品功能
61 | 将配置中的showProduct值设置为0即可。如图
62 | 
63 | 同时还需要将产品中心的导航移除。打开app.json,找到产品中心代码,并将它们移除,如图
64 | 
65 | 还需要将首页导航图标注释。打开common.js,格式化它。如图
66 | 
67 |
68 | 8. 首页幻灯片设置
69 | 配置首页幻灯片可以让小程序更突出,也可以让首屏加载更快。幻灯片同时支持在织梦后台配置,织梦后台的文档,添加幻灯属性后,即可替代config.js的配置。
70 | 每张幻灯片由4个字段组成,model的值可以是article或product,id的值是对应的文章或产品id,title为显示的名称,title可以不填,logo为图片地址。如图
71 | 
72 |
73 | 9. 首页导航图标设置
74 |
75 | 首页导航图标可以让更重要的信息直达。建议配置不要超过5个。
76 | 每个图标由3个字段组成,url为小程序的链接,title为名称,logo为图片地址,尽量使用网络图片地址。如图
77 | 
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 &&
68 | )
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/components/empty/index.scss:
--------------------------------------------------------------------------------
1 | .empty {
2 | display: flex;
3 | flex-direction: column;
4 | align-items: center;
5 | padding: 120px 0;
6 | .empty-image {
7 | width: 160px;
8 | height: 160px;
9 | margin-bottom: 20px;
10 | }
11 | .empty-text{
12 | font-size: 32px;
13 | }
14 | .empty-text2{
15 | margin-top: 20px;
16 | font-size: 24px;
17 | }
18 | }
--------------------------------------------------------------------------------
/src/components/empty/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View, Text, Image } from '@tarojs/components'
3 | import './index.scss'
4 |
5 | export default class Empty extends Component {
6 | static defaultProps = {
7 | image: '',
8 | title: '没有内容',
9 | description: ''
10 | }
11 |
12 | static options = {
13 | addGlobalClass: true,
14 | }
15 |
16 | render() {
17 | const { image, title, description } = this.props
18 |
19 | return (
20 |
21 | {image && }
22 | {title}
23 | {description && {description}}
24 |
25 | )
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/components/footer/index.scss:
--------------------------------------------------------------------------------
1 | @import '../../var.scss';
2 | .footer{
3 | .footer-text{
4 | line-height: 56px;
5 | font-size: 28px;
6 | }
7 | .footer-service{
8 | padding-top: 30px;
9 | padding-bottom: 20px;
10 | font-size: 24px;
11 | text-align: center;
12 | color: #999;
13 | }
14 | }
15 | .fixed-fab{
16 | position: fixed;
17 | right: 40px;
18 | bottom: 40px;
19 | width: 80px;
20 | height: 80px;
21 | border-radius: 50%;
22 | background: rgba($primary, 0.5);
23 | .fab-icon{
24 | padding: 10px;
25 | width: 60px;
26 | height: 60px;
27 | }
28 | }
--------------------------------------------------------------------------------
/src/components/footer/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import Taro from '@tarojs/taro'
3 | import { View, Text, Image } from '@tarojs/components'
4 | import Version from '../../version'
5 | import './index.scss'
6 |
7 | export default class Footer extends Component {
8 | static defaultProps = {
9 | company: '',
10 | address: '',
11 | mobile: '',
12 | // footerText: '',
13 | // copyright: '',
14 | }
15 |
16 | static options = {
17 | addGlobalClass: true,
18 | }
19 |
20 | phoneCall = () => {
21 | Taro.makePhoneCall({
22 | phoneNumber: this.props.mobile
23 | })
24 | }
25 |
26 | showPath = () => {
27 | let a = Taro.getCurrentPages(),
28 | n = {},
29 | r = a[a.length - 1],
30 | g = r.route,
31 | s = r.options || {},
32 | c = s && JSON.stringify(s),
33 | v = Version;
34 | let u = ["该小程序由邦权小程序开发团队开发并提供技术支持,联系微信:websafety","小程序版本号:" + v, "页面路径", g]
35 | c && "{}" !== c && u.push("页面参数", c), n.page = g, n.query = s;
36 | Taro.showModal({
37 | title: "小程序信息",
38 | content: u.join("\n"),
39 | confirmText: "确定",
40 | showCancel: !1,
41 | success: function() {
42 | Taro.setClipboardData({
43 | data: JSON.stringify(n)
44 | });
45 | }
46 | });
47 | }
48 |
49 | render() {
50 | //const { company, address, mobile, footerText, copyright } = this.props
51 |
52 | return (
53 |
54 | 邦权小程序开发团队提供技术支持
55 |
56 |
57 |
58 |
59 | )
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/components/header/index.scss:
--------------------------------------------------------------------------------
1 | @import '../../var.scss';
2 | .header{
3 | height: 80px;
4 | padding: 10px 20px;
5 | display: flex;
6 | border-top: $border;
7 | background-color: #fff;
8 | .header-logo{
9 | height: 80px;
10 | width: 160px;
11 | overflow: hidden;
12 | .logo{
13 | height: 80px;
14 | width: 160px;
15 | }
16 | }
17 | .header-text{
18 | line-height: 80px;
19 | color: $secondary;
20 | flex: 1;
21 | padding:0 20px;
22 | }
23 | .header-contact{
24 | width: 220px;
25 | .mobile-tips{
26 | font-size: 24px;
27 | }
28 | .mobile{
29 | font-size: 36px;
30 | color: $primary;
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/src/components/header/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import Taro from '@tarojs/taro'
3 | import { View, Image } from '@tarojs/components'
4 | import './index.scss'
5 |
6 | export default class Header extends Component {
7 | static defaultProps = {
8 | logo: '',
9 | headerText: '',
10 | mobile: ''
11 | }
12 |
13 | static options = {
14 | addGlobalClass: true,
15 | }
16 |
17 | gotoHome = () => {
18 | Taro.natigateTo({
19 | url: '/pages/index/index'
20 | })
21 | }
22 |
23 | phoneCall = () => {
24 | Taro.makePhoneCall({
25 | phoneNumber: this.props.mobile
26 | })
27 | }
28 |
29 | render() {
30 | const { logo, headerText, mobile } = this.props
31 | return (
32 |
33 |
34 | {headerText}
35 |
36 | 联系电话:
37 | {mobile}
38 |
39 |
40 | )
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/components/product/index.scss:
--------------------------------------------------------------------------------
1 | @import '../../var.scss';
2 | .product-list{
3 | display: flex;
4 | flex-wrap: wrap;
5 | padding: 10px;
6 | padding-bottom: 0;
7 | margin-bottom: -10px;
8 | .product-item{
9 | width: 50%;
10 | .inner{
11 | margin: 10px;
12 | padding: 20px;
13 | background: #fff;
14 | border-radius: 10px;
15 | }
16 | .product-image{
17 | position: relative;
18 | height: 0;
19 | padding-bottom: 75%;
20 | border: $border;
21 | border-radius: 6px;
22 | overflow: hidden;
23 | .image-item{
24 | position: absolute;
25 | top: 0;
26 | width: 100%;
27 | height: 100%;
28 | }
29 | }
30 | .product-meta{
31 | padding: 10px 0;
32 | .product-title{
33 | text-overflow: ellipsis;
34 | white-space: nowrap;
35 | overflow: hidden;
36 | }
37 | .product-desc{
38 | font-size: 24px;
39 | color: #999;
40 | display: -webkit-box;
41 | -webkit-box-orient: vertical;
42 | -webkit-line-clamp: 2;
43 | overflow: hidden;
44 | }
45 | }
46 | }
47 | .product-item.column-1{
48 | width: 100%;
49 | .image-item{
50 | width: 100%;
51 | }
52 | }
53 | }
--------------------------------------------------------------------------------
/src/components/product/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 | export default class Product extends Component {
8 | static defaultProps = {
9 | products: [],
10 | loading: !1,
11 | column: 2,
12 | }
13 |
14 | static options = {
15 | addGlobalClass: true,
16 | }
17 |
18 | gotoProduct = (e) => {
19 | utils.navigate({
20 | url: '/pages/product/index?id=' + e
21 | })
22 | }
23 |
24 | render() {
25 | const { products, loading, column } = this.props
26 |
27 | return (
28 |
29 | {products.length > 0 &&
30 | {products.map((item, index) => {
31 | return
32 |
33 |
34 |
35 |
36 |
37 | {item.title}
38 |
39 |
40 |
41 | })}
42 | }
43 | {loading && }
44 | {!products.length && !loading && }
45 |
46 | )
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/components/richhtml/image/index.scss:
--------------------------------------------------------------------------------
1 | .rich-img {
2 | max-width: 100% !important;
3 | // height: auto !important;
4 | }
--------------------------------------------------------------------------------
/src/components/richhtml/image/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import Taro from '@tarojs/taro'
3 | import { Image } from '@tarojs/components'
4 | import './index.scss'
5 |
6 | export default class RichImage extends Component {
7 |
8 | // static defaultProps = {
9 | // attrs: {},
10 | // nodes: [],
11 | // }
12 |
13 | state = {
14 | width: 0,
15 | height: 0,
16 | }
17 |
18 | static options = {
19 | addGlobalClass: true,
20 | }
21 |
22 | onLoad = (e) => {
23 | if(e.detail){
24 | let {width, height} = e.detail
25 |
26 | if(width >= 355){
27 | this.setState({
28 | width: 0,
29 | // height: 355*height/width
30 | })
31 | }else{
32 | this.setState({
33 | width: width + 'px',
34 | // height: height + 'px',
35 | })
36 | }
37 | }
38 | }
39 |
40 | preview = (src, e) => {
41 | e.stopPropagation()
42 | //h5下,在pc端无法关闭的bug, 所以不支持预览图片
43 | if (Taro.ENV_TYPE.WEB !== Taro.getEnv()) {
44 | Taro.previewImage({
45 | urls: [src]
46 | })
47 | }
48 | }
49 |
50 | render () {
51 | const { attrs = {} } = this.props
52 | const {width, height} = this.state
53 | return (
54 |
55 | )
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/components/richhtml/index.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/src/components/richhtml/index.scss
--------------------------------------------------------------------------------
/src/components/richhtml/index.tsx:
--------------------------------------------------------------------------------
1 | import { Component } from 'react'
2 | import { View } from '@tarojs/components'
3 | import RichView from './view'
4 | import html2Json from './utils/html2Json'
5 | import './index.scss'
6 |
7 | export default class SeoRichHtml extends Component {
8 | static defaultProps = {
9 | fullscreen: false,
10 | bgColor: '',
11 | content: '',
12 | }
13 |
14 | static options = {
15 | addGlobalClass: true,
16 | }
17 |
18 | state = {
19 | nodes: [],
20 | }
21 |
22 | componentWillMount() {
23 | const { content } = this.props
24 | let nodes = html2Json(content)
25 | this.setState({
26 | nodes: nodes
27 | })
28 | }
29 |
30 | componentWillReceiveProps(nextProps) {
31 | if (nextProps.content != this.props.content) {
32 | let nodes = html2Json(nextProps.content)
33 | this.setState({
34 | nodes: nodes
35 | })
36 | }
37 | }
38 |
39 | render() {
40 | const { fullscreen, bgColor } = this.props
41 | const { nodes } = this.state
42 |
43 | let scontentStyle = bgColor ? ' background-color:' + bgColor + ';' : ''
44 | scontentStyle += !fullscreen ? 'padding:5px;' : ''
45 | return
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/components/richhtml/utils/html2Json.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * html2Json 改造来自: https://github.com/Jxck/html2json
3 | */
4 | import HTMLParser from './htmlParser'
5 | // Block Elements - HTML 5 not video not audio
6 | var block = makeMap("address,code,article,applet,aside,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul");
7 |
8 | // Inline Elements - HTML 5- not-img not-a
9 | var inline = makeMap("abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,del,dfn,em,font,i,iframe,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var");
10 |
11 | function makeMap(str) {
12 | var obj = {}, items = str.split(",");
13 | for (var i = 0; i < items.length; i++)
14 | obj[items[i]] = true;
15 | return obj;
16 | }
17 |
18 | function removeDOCTYPE(html) {
19 | return html
20 | .replace(/<\?xml.*\?>\n/, '')
21 | .replace(/<.*!doctype.*\>\n/, '')
22 | .replace(/<.*!DOCTYPE.*\>\n/, '');
23 | }
24 |
25 | function trimHtml(html) {
26 | return html
27 | .replace(/\r?\n+/g, '')
28 | .replace(//ig, '')
29 | .replace(/\/\*.*?\*\//ig, '')
30 | .replace(/[ ]+');
44 | str = str.replace(/•/g, '•');
45 |
46 | return str;
47 | }
48 |
49 | function getTagName(name) {
50 | let tagName = name;
51 | if(inline[name]){
52 | tagName = 'text'
53 | }else if(block[name]){
54 | tagName = 'view'
55 | }
56 |
57 | return tagName;
58 | }
59 |
60 | function html2json(html) {
61 | if(!html){
62 | return [];
63 | }
64 | //处理字符串
65 | html = removeDOCTYPE(html);
66 | html = trimHtml(html);
67 | //html = strcharacterDiscode(html);
68 | //生成node节点
69 | var bufArray = [];
70 | var results = {
71 | children: [],
72 | };
73 | var index = 0;
74 | HTMLParser(html, {
75 | start: function (name, attrs, unary) {
76 | //debug(tag, attrs, unary);
77 | // node for this element
78 | var node = {
79 | tagName: getTagName(name),
80 | name: name,
81 | attrs: {}
82 | };
83 | if (name === 'pre'){
84 | //修复rich-text无法pre问题
85 | node.name = 'code'
86 | }
87 |
88 | if (bufArray.length === 0) {
89 | node.index = index.toString()
90 | index += 1
91 | } else {
92 | var parent = bufArray[0];
93 | if (parent.children === undefined) {
94 | parent.children = [];
95 | }
96 | node.index = parent.index + '.' + parent.children.length
97 | }
98 |
99 | if (attrs.length !== 0) {
100 | node.attrs = attrs.reduce(function (pre, attr) {
101 | var name = attr.name;
102 | var value = attr.value;
103 | if (name.indexOf("-")) {
104 | let nameArr = name.split("-")
105 | for (let item of nameArr) {
106 | item = item[0].toUpperCase() + item.substring(1, item.length)
107 | }
108 | name = nameArr.join()
109 | }
110 | if (name == 'class') {
111 | value = 'rich-' + node.name + ' ' + value
112 | }
113 | // if attr already exists
114 | // merge it
115 | if (pre[name]) {
116 | pre[name] = pre[name] + value;
117 | } else {
118 | // not exist, put it
119 | pre[name] = value;
120 | }
121 |
122 | return pre;
123 | }, {});
124 | }
125 |
126 | //class 每一个都要加上
127 | node.attrs.class = 'rich-' + node.name + (node.attrs.class ? ' ' + node.attrs.class : '')
128 |
129 | //对img添加额外数据
130 | if (node.name === 'img') {
131 | var imgUrl = node.attrs.src;
132 | if (imgUrl[0] == '') {
133 | imgUrl.splice(0, 1);
134 | }
135 |
136 | node.attrs.src = imgUrl;
137 | }
138 |
139 | // 处理font标签样式属性
140 | if (node.name === 'font') {
141 | var fontSize = ['x-small', 'small', 'medium', 'large', 'x-large', 'xx-large', '-webkit-xxx-large'];
142 | var styleAttrs = {
143 | 'color': 'color',
144 | 'face': 'font-family',
145 | 'size': 'font-size'
146 | };
147 |
148 | if (typeof node.attrs != "undefined" && node.attrs != 'null' && node.attrs != '') {
149 | if (!node.attrs.style) node.attrs.style = '';
150 | }
151 |
152 | if (typeof node.attrs != "undefined" && node.attrs != 'null' && node.attrs != '') {
153 | for (var key in styleAttrs) {
154 | if (node.attrs[key]) {
155 | var value = key === 'size' ? fontSize[node.attrs[key] - 1] : node.attrs[key];
156 | node.attrs.style += styleAttrs[key] + ': ' + value + ';';
157 | }
158 | }
159 | }
160 | }
161 |
162 | //临时记录source资源
163 | if (node.name === 'source') {
164 | results.source = node.attrs.src;
165 | }
166 |
167 | if (unary) {
168 | // if this tag doesn't have end tag
169 | // like
170 | // add to parents
171 | var parent = bufArray[0] || results;
172 | if (parent.children === undefined) {
173 | parent.children = [];
174 | }
175 | parent.children.push(node);
176 | } else {
177 | bufArray.unshift(node);
178 | }
179 | },
180 | end: function (name) {
181 | //debug(tag);
182 | // merge into parent tag
183 | var node = bufArray.shift();
184 | if (node.name !== name) console.error('invalid state: mismatch end tag');
185 |
186 | //当有缓存source资源时于于video补上src资源
187 | if (node.name === 'video' && results.source) {
188 | node.attrs.src = results.source;
189 | delete results.source;
190 | }
191 |
192 | if (bufArray.length === 0) {
193 | results.children.push(node);
194 | } else {
195 | var parent = bufArray[0];
196 | if (parent.children === undefined) {
197 | parent.children = [];
198 | }
199 | parent.children.push(node);
200 | }
201 | //table 强制修正td宽度
202 | if(name === 'tr') {
203 | let single = (100/node.children.length).toFixed(5)
204 | for (let item of node.children) {
205 | if (!item.attrs){
206 | item.attrs = {}
207 | }
208 | item.attrs.style = 'width: '+single+'%;' + (item.attrs.style ? item.attrs.style : '')
209 | }
210 | }
211 | },
212 | chars: function (text) {
213 | text = strcharacterDiscode(text);
214 | var node = {
215 | type: 'text',
216 | text: text,
217 | };
218 |
219 | if (bufArray.length === 0) {
220 | node.index = index.toString()
221 | index += 1
222 | results.children.push(node);
223 | } else {
224 | var parent = bufArray[0];
225 | if (parent.children === undefined) {
226 | parent.children = [];
227 | }
228 | node.index = parent.index + '.' + parent.children.length
229 | parent.children.push(node);
230 | }
231 | },
232 | comment: function (text) {
233 | // var node = {
234 | // node: 'comment',
235 | // text: text,
236 | // };
237 | // var parent = bufArray[0];
238 | // if (parent.children === undefined) {
239 | // parent.children = [];
240 | // }
241 | // parent.children.push(node);
242 | },
243 | });
244 | return results.children;
245 | };
246 |
247 | export default html2json
--------------------------------------------------------------------------------
/src/components/richhtml/utils/htmlParser.ts:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * htmlParser改造自: https://github.com/blowsie/Pure-JavaScript-HTML5-Parser
4 | *
5 | * github地址: https://github.com/icindy/wxParse
6 | *
7 | * htmlParser 2次改造自wxParse 20181003
8 | */
9 | import xss from 'xss';
10 | // Regular Expressions for parsing tags and attributes
11 | //var startTag = /^<([-A-Za-z0-9_]+)((?:\s+[a-zA-Z_:][-a-zA-Z0-9_:.]*(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/,
12 | var startTag = /^<([-A-Za-z0-9_]+)((?:\s+[a-zA-Z_:][-a-zA-Z0-9_:.]*(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/,
13 | endTag = /^<\/([-A-Za-z0-9_]+)[^>]*>/,
14 | attr = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g;
15 |
16 | // Empty Elements - HTML 5
17 | var empty = makeMap("area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr");
18 |
19 | // Block Elements - HTML 5
20 | var block = makeMap("a,address,code,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video");
21 |
22 | // Inline Elements - HTML 5
23 | var inline = makeMap("abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var");
24 |
25 | // Elements that you can, intentionally, leave open
26 | // (and which close themselves)
27 | var closeSelf = makeMap("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr");
28 |
29 | // Attributes that have their values filled in disabled="disabled"
30 | var fillAttrs = makeMap("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected");
31 |
32 | // Special Elements (can contain anything)
33 | var special = makeMap("wxxxcode-style,script,style,view,scroll-view,block");
34 |
35 | function HTMLParser(html, handler) {
36 | let index, chars, match: any = {};
37 | let stack: any[] = [];
38 | //先过滤html
39 | html = xss(html, {
40 | whiteList: Object.assign({
41 | strike: ['datetime']
42 | }, xss.getDefaultWhiteList()),
43 | //支持
44 | onIgnoreTagAttr: function(tag, name, value, isWhiteAttr) {
45 | if (name.substr(0, 5) === "data-") {
46 | // 通过内置的escapeAttrValue函数来对属性值进行转义
47 | return name + '="' + xss.escapeAttrValue(value) + '"';
48 | }
49 | if (name.substr(0, 5) === "style") {
50 | // 通过内置的escapeAttrValue函数来对属性值进行转义
51 | return name + '="' + xss.escapeAttrValue(value) + '"';
52 | }
53 | },
54 | stripIgnoreTag: true
55 | });
56 | var last = html;
57 | stack.last = function () {
58 | return this[this.length - 1];
59 | };
60 |
61 | while (html) {
62 | chars = true;
63 |
64 | // Make sure we're not in a script or style element
65 | if (!stack.last() || !special[stack.last()]) {
66 |
67 | // Comment
68 | if (html.indexOf("");
70 |
71 | if (index >= 0) {
72 | if (handler.comment)
73 | handler.comment(html.substring(4, index));
74 | html = html.substring(index + 3);
75 | chars = false;
76 | }
77 |
78 | // end tag
79 | } else if (html.indexOf("") == 0) {
80 | match = html.match(endTag);
81 | //a标签会误伤
82 | if (match) {
83 | html = html.substring(match[0].length);
84 | match[0].replace(endTag, parseEndTag);
85 | chars = false;
86 | }
87 |
88 | // start tag
89 | } else if (html.indexOf("<") == 0) {
90 | match = html.match(startTag);
91 | if (match) {
92 | html = html.substring(match[0].length);
93 | match[0].replace(startTag, parseStartTag);
94 | chars = false;
95 | }
96 | }
97 |
98 | if (chars) {
99 | index = html.indexOf("<");
100 | var text = ''
101 | while (index === 0) {
102 | text += "<";
103 | html = html.substring(1);
104 | index = html.indexOf("<");
105 | }
106 | text += index < 0 ? html : html.substring(0, index);
107 | html = index < 0 ? "" : html.substring(index);
108 |
109 | if (handler.chars)
110 | handler.chars(text);
111 | }
112 |
113 | } else {
114 |
115 | html = html.replace(new RegExp("([\\s\\S]*?)<\/" + stack.last() + "[^>]*>"), function (all, text) {
116 | text = text.replace(/|/g, "$1$2");
117 | if (handler.chars)
118 | handler.chars(text);
119 |
120 | return "";
121 | });
122 |
123 |
124 | parseEndTag("", stack.last());
125 | }
126 |
127 | if (html == last)
128 | throw "Parse Error: " + html;
129 | last = html;
130 | }
131 |
132 | // Clean up any remaining tags
133 | parseEndTag("", "");
134 |
135 | function parseStartTag(tag, tagName, rest, unary) {
136 | tagName = tagName.toLowerCase();
137 |
138 | if (block[tagName]) {
139 | while (stack.last() && inline[stack.last()]) {
140 | parseEndTag("", stack.last());
141 | }
142 | }
143 |
144 | if (closeSelf[tagName] && stack.last() == tagName) {
145 | parseEndTag("", tagName);
146 | }
147 |
148 | unary = empty[tagName] || !!unary;
149 |
150 | if (!unary)
151 | stack.push(tagName);
152 |
153 | if (handler.start) {
154 | let attrs: any = [];
155 |
156 | rest.replace(attr, function (match, name) {
157 | var value = arguments[2] ? arguments[2] :
158 | arguments[3] ? arguments[3] :
159 | arguments[4] ? arguments[4] :
160 | fillAttrs[name] ? name : "";
161 |
162 | attrs.push({
163 | name: name,
164 | value: value,
165 | escaped: value.replace(/(^|[^\\])"/g, '$1\\\"') //"
166 | });
167 | });
168 |
169 | if (handler.start) {
170 | handler.start(tagName, attrs, unary);
171 | }
172 |
173 | }
174 | }
175 |
176 | function parseEndTag(tag, tagName) {
177 | // If no tag name is provided, clean shop
178 | if (!tagName)
179 | var pos = 0;
180 |
181 | // Find the closest opened tag of the same type
182 | else {
183 | tagName = tagName.toLowerCase();
184 | for (var pos = stack.length - 1; pos >= 0; pos--)
185 | if (stack[pos] == tagName)
186 | break;
187 | }
188 | if (pos >= 0) {
189 | // Close all the open elements, up the stack
190 | for (var i = stack.length - 1; i >= pos; i--)
191 | if (handler.end)
192 | handler.end(stack[i]);
193 |
194 | // Remove the open elements from the stack
195 | stack.length = pos;
196 | }
197 | }
198 | };
199 |
200 |
201 | function makeMap(str) {
202 | var obj = {}, items = str.split(",");
203 | for (var i = 0; i < items.length; i++)
204 | obj[items[i]] = true;
205 | return obj;
206 | }
207 |
208 | export default HTMLParser
209 |
--------------------------------------------------------------------------------
/src/components/richhtml/video/index.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/src/components/richhtml/video/index.scss
--------------------------------------------------------------------------------
/src/components/richhtml/video/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Video } from '@tarojs/components'
3 | import './index.scss'
4 |
5 | export default class RichVideo extends Component {
6 |
7 | static defaultProps = {
8 | attrs: {},
9 | nodes: [],
10 | }
11 |
12 | static options = {
13 | addGlobalClass: true,
14 | }
15 |
16 | render () {
17 | const { attrs, nodes } = this.props
18 |
19 | return (
20 |
21 | )
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/richhtml/view/index.scss:
--------------------------------------------------------------------------------
1 | $primary: #266AAE;
2 | $background: #f6f6f6;
3 |
4 | .rich-p {
5 | margin-bottom: 20px;
6 | word-break: break-all;
7 | }
8 | .rich-text,.rich-abbr,.rich-del,.rich-em,.rich-font,.rich-i,.rich-label,.rich-span,.rich-strike,.rich-strong,.rich-sub,.rich-sup,.rich-u,.rich-b, .rich-a{
9 | display: inline;
10 | }
11 | /* pre */
12 | .rich-pre,.rich-code {
13 | font-size: 28px;
14 | margin-top: 20px;
15 | margin-bottom: 20px;
16 | padding: 10px;
17 | background: $background;
18 | white-space: pre-wrap;
19 | word-break: break-all;
20 | border-left: 2px solid #3273dc;
21 | }
22 | /* 图片 */
23 | .rich-img {
24 | max-width: 100% !important;
25 | // height: auto !important;
26 | }
27 | /* 视频 */
28 | .rich-video {
29 | width: 100%;
30 | }
31 | /* 超链接 */
32 | .rich-a {
33 | color: $primary;
34 | }
35 | /* 表格 */
36 | .rich-table {
37 | table-layout: fixed; border-collapse: collapse; display: table;
38 | }
39 | .rich-tbody{
40 | display: table-row-group;
41 | vertical-align: middle;
42 | border-color: inherit;
43 | }
44 | .rich-head{
45 | display: table-header-group;
46 | vertical-align: middle;
47 | border-color: inherit;
48 | }
49 | .rich-foot{
50 | display: table-footer-group;
51 | vertical-align: middle;
52 | border-color: inherit;
53 | }
54 | .rich-td,.rich-th {
55 | display: table-cell; vertical-align: inherit; padding:5px 10px; font-size:28px; border: 1px solid #e0e0e0; word-break: break-all;
56 | }
57 | .rich-th {
58 | background:$background;
59 | vertical-align: inherit;
60 | font-weight: bold;
61 | text-align: center;
62 | text-align: -internal-center;
63 | }
64 | .rich-tr{
65 | display: table-row;
66 | vertical-align: inherit;
67 | border-color: inherit;
68 | }
69 | .rich-h1,.rich-h2,.rich-h3,.rich-h4,.rich-h5,.rich-h6 {
70 | display: block;
71 | margin-block-start: 1em;
72 | margin-block-end: 1em;
73 | font-weight: bold;
74 | }
75 | .rich-h1{
76 | font-size: 2em;
77 | }
78 | .rich-h2{
79 | font-size: 1.5em;
80 | }
81 | .rich-h3{
82 | font-size: 1.25em;
83 | }
84 | .rich-h4{
85 | font-size: 1.17em;
86 | }
87 | .rich-h5{
88 | font-size: 1em;
89 | }
90 | .rich-h6{
91 | font-size: 0.85em;
92 | }
93 | .rich-blockquote {
94 | margin-bottom: 20px;
95 | padding: 20px;
96 | border-left: 10px solid $primary;
97 | border-radius: 0 4px 4px 0;
98 | background-color: $background;
99 | }
100 | .rich-ul,.rich-ol {
101 | list-style-type: disc;
102 | margin-top: 1em;
103 | margin-bottom: 1em;
104 | padding-left: 80px;
105 | }
106 | .rich-ol{
107 | list-style-type: decimal;
108 | }
109 | .rich-li{
110 | display: list-item;
111 | text-align: -webkit-match-parent;
112 | }
113 | .rich-abbr{
114 | text-decoration: underline dotted;
115 | }
116 | .rich-b, .rich-strong, .rich-label{
117 | font-weight: bold;
118 | }
119 | .rich-del, .rich-strike, .rich-s {
120 | text-decoration: line-through;
121 | }
122 | .rich-em, .rich-i{
123 | font-style: italic;
124 | }
125 | .rich-sub{
126 | vertical-align: sub;
127 | font-size: smaller;
128 | }
129 | .rich-sup{
130 | vertical-align: super;
131 | font-size: smaller;
132 | }
133 | .rich-u {
134 | text-decoration: underline;
135 | }
--------------------------------------------------------------------------------
/src/components/richhtml/view/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import Taro from '@tarojs/taro'
3 | import { View, Text, RichText } from '@tarojs/components'
4 | import RichImage from '../image'
5 | import SeoVideo from '../video'
6 | import './index.scss'
7 |
8 | export default class RichView extends Component {
9 | static defaultProps = {
10 | name: '',
11 | attrs: {},
12 | nodes: []
13 | }
14 |
15 | config = {
16 | usingComponents: {
17 | 'rich-view': './index'
18 | }
19 | }
20 |
21 | static options = {
22 | addGlobalClass: true,
23 | }
24 |
25 | jumpLink = (attrs) => {
26 | let { href, dataType = '', dataId = 0 } = attrs
27 | if (href) {
28 | //这是一个连接
29 | if(href.indexOf('http') !== -1){
30 | return Taro.navigateTo({
31 | url: '/pages/browser/index?url=' + href
32 | })
33 | }else if(href.indexOf('/pages') !== -1) {
34 | Taro.navigateTo({
35 | url: href
36 | }).catch(err => {
37 | Taro.switchTab({
38 | url: href
39 | })
40 | })
41 | }
42 | } else if (dataType && dataId) {
43 | return Taro.navigateTo({
44 | url: '/pages/' + dataType + '/index?id=' + dataId
45 | })
46 | } else {
47 | return Taro.switchTab({
48 | url: '/pages/index/index'
49 | })
50 | }
51 | }
52 |
53 | renderText(text) {
54 | return {text}
55 | }
56 |
57 | renderVideo(attrs) {
58 | return
59 | }
60 |
61 | renderLink(attrs, name, nodes = []) {
62 | return
63 | {nodes && nodes.length > 0 && }
64 |
65 | }
66 |
67 | render() {
68 | const { attrs = {}, nodes = [], name } = this.props
69 |
70 | return 1 && name ? '' : 'rich-' + name)} style={attrs.style}>
71 | {nodes && nodes.length > 0 && nodes.map((item, index) => {
72 | return item &&
73 | item.type ? this.renderText(item.text)
74 | : item.tagName == 'img' ?
75 | : item.tagName == 'video' ? this.renderVideo(item.attrs)
76 | : item.tagName == 'a' ? this.renderLink(item.attrs, item.name, item.children || [])
77 | : item.name == 'table' ?
78 | : (item.tagName == 'text' || item.tagName == 'view' || item.tagName == 'pre') ?
79 | : item.children && item.children.length ?
80 | : ''
81 | })}
82 |
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/config.dist.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 说明:这里的配置,如果后台也同时配置了,则会被后台相同的配置替代,这里的配置可以加速小程序显示
3 | */
4 | const appConfig = {
5 | /* API地址 */
6 | api: 'https://www.taokee.top/api/app',
7 | /* 基本配置 */
8 | /* 小程序logo */
9 | logo: 'https://www.taokee.top/img/logo.png',
10 | /* 小程序名称 */
11 | title: '织梦网站小程序制作',
12 | /* 首页标题 */
13 | seoTitle: '织梦网站小程序制作:量身定制百度智能小程序、微信小程序',
14 | /* 首页关键词 */
15 | keywords: '织梦小程序开发,织梦小程序源码,织梦小程序教程,织梦小程序定制',
16 | /* 首页描述 */
17 | description: '织梦小程序专门为织梦网站开发微信小程序,百度智能小程序,拥有多年小程序开发经验,众多成功案例。找织梦小程序开发公司,选择织梦小程序开发团队。',
18 | /* 联系方式 */
19 | /* 联系电话 */
20 | mobile: '17097218761',
21 | /* 联系微信 */
22 | wechat: '17097218761',
23 | /* 公司名称 */
24 | company: '织梦网站小程序制作',
25 | /* 公司地址 */
26 | address: '广东深圳',
27 | /* 内容设置 */
28 | /* 关于我们的单页/分类id */
29 | aboutId: '1',
30 | /* 联系我们单页/分类id */
31 | contactId: '2',
32 | /* 排除不显示的分类id,多个分类id请用英文的,隔开 */
33 | ignoreCategory: '3,4',
34 | /* 是否显示产品, 1显示,0不显示 */
35 | showProduct: 1,
36 | /* 产品模型id,默认是6 */
37 | productModelId: '6',
38 | /* 产品顶级分类id, 只有在没有使用产品模型的时候需要填写 */
39 | productCategory: '',
40 | // /* 是否显示案例,1显示,0不显示 */
41 | // showCase: 1,
42 | // /* 案例分类顶级id,不填写有可能不能正常显示案例 */
43 | // caseCategory: '9',
44 | /* 需要显示在首页的分类,多个分类用,隔开 */
45 | indexCategory: '1,7,8',
46 | /* 首页幻灯片,可以显示多个, 如果后台的产品设置了幻灯显示,则这里配置失效
47 | 每张幻灯片由4个字段组成,model的值可以是article或product,id的值是对应的文章或产品id,title为显示的名称,logo为图片地址 */
48 | indexBanner: [
49 | {
50 | id: 0,
51 | model: 'article',
52 | title: '',
53 | logo: 'https://www.taokee.top/img/banner1.jpg'
54 | },
55 | {
56 | id: 0,
57 | model: 'article',
58 | logo: 'https://www.taokee.top/img/banner2.jpg'
59 | }
60 | ],
61 | /* 首页导航图标,建议不超过5个
62 | 每个图标由3个字段组成,url为小程序的链接,title为名称,logo为图片地址 */
63 | indexNav: [
64 | {
65 | url: '/pages/products/index',
66 | title: '产品中心',
67 | logo: '/assets/nav-1.png'
68 | },
69 | {
70 | url: '/pages/articles/index',
71 | title: '文章中心',
72 | logo: '/assets/nav-2.png'
73 | },
74 | {
75 | url: '/pages/about/index',
76 | title: '关于我们',
77 | logo: '/assets/nav-3.png'
78 | },
79 | {
80 | url: '/pages/contact/index',
81 | title: '联系我们',
82 | logo: '/assets/nav-4.png'
83 | },
84 | ],
85 | }
86 |
87 | export default appConfig
88 |
--------------------------------------------------------------------------------
/src/config.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 说明:这里的配置,如果后台也同时配置了,则会被后台相同的配置替代,这里的配置可以加速小程序显示
3 | */
4 | const appConfig = {
5 | /* API地址 */
6 | api: 'https://www.vanapp.cn/appclient.php',
7 | /* 基本配置 */
8 | /* 小程序logo */
9 | logo: 'https://www.vanapp.cn/skin/images/logo.png',
10 | /* 小程序名称 */
11 | title: '织梦网站小程序制作',
12 | /* 首页标题 */
13 | seoTitle: '织梦网站小程序制作:量身定制百度智能小程序、微信小程序',
14 | /* 首页关键词 */
15 | keywords: '织梦小程序开发,织梦小程序源码,织梦小程序教程,织梦小程序定制',
16 | /* 首页描述 */
17 | description: '织梦小程序专门为织梦网站开发微信小程序,百度智能小程序,拥有多年小程序开发经验,众多成功案例。找织梦小程序开发公司,选择织梦小程序开发团队。',
18 | /* 联系方式 */
19 | /* 联系电话 */
20 | mobile: '17097218761',
21 | /* 联系微信 */
22 | wechat: '17097218761',
23 | /* 公司名称 */
24 | company: '织梦网站小程序制作',
25 | /* 公司地址 */
26 | address: '广东深圳',
27 | /* 内容设置 */
28 | /* 关于我们的单页/分类id */
29 | aboutId: '23',
30 | /* 联系我们单页/分类id */
31 | contactId: '19',
32 | /* 排除不显示的分类id,多个分类id请用英文的,隔开 */
33 | ignoreCategory: '7',
34 | /* 是否显示产品, 1显示,0不显示 */
35 | showProduct: 1,
36 | /* 产品模型id,默认是6 */
37 | productModelId: '2',
38 | /* 产品顶级分类id, 只有在没有使用产品模型的时候需要填写 */
39 | productCategory: '',
40 | // /* 是否显示案例,1显示,0不显示 */
41 | showCase: 1,
42 | // /* 案例分类顶级id,不填写有可能不能正常显示案例 */
43 | // caseCategory: '9',
44 | /* 需要显示在首页的分类,多个分类用,隔开 */
45 | indexCategory: '1,7,8',
46 | /* 首页幻灯片,可以显示多个, 如果后台的产品设置了幻灯显示,则这里配置失效
47 | 每张幻灯片由4个字段组成,model的值可以是article或product,id的值是对应的文章或产品id,title为显示的名称,logo为图片地址 */
48 | indexBanner: [
49 | {
50 | id: 0,
51 | model: 'article',
52 | title: '',
53 | logo: 'https://www.taokee.top/img/banner1.jpg'
54 | },
55 | {
56 | id: 0,
57 | model: 'article',
58 | logo: 'https://www.taokee.top/img/banner2.jpg'
59 | }
60 | ],
61 | /* 首页导航图标,建议不超过5个
62 | 每个图标由3个字段组成,url为小程序的链接,title为名称,logo为图片地址 */
63 | indexNav: [
64 | {
65 | url: '/pages/products/index',
66 | title: '产品中心',
67 | logo: '/assets/nav-1.png'
68 | },
69 | {
70 | url: '/pages/articles/index',
71 | title: '文章中心',
72 | logo: '/assets/nav-2.png'
73 | },
74 | {
75 | url: '/pages/about/index',
76 | title: '关于我们',
77 | logo: '/assets/nav-3.png'
78 | },
79 | {
80 | url: '/pages/contact/index',
81 | title: '联系我们',
82 | logo: '/assets/nav-4.png'
83 | },
84 | ],
85 | }
86 |
87 | export default appConfig
88 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/pages/about/index.config.ts:
--------------------------------------------------------------------------------
1 | export default definePageConfig({
2 | navigationBarTitleText: '关于我们'
3 | })
4 |
--------------------------------------------------------------------------------
/src/pages/about/index.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/src/pages/about/index.scss
--------------------------------------------------------------------------------
/src/pages/about/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import Taro from '@tarojs/taro'
3 | import { View, Text, Image, Block } from '@tarojs/components'
4 | import { AtLoadMore } from 'taro-ui'
5 | import Container from '../../components/container'
6 | import RichHtml from '../../components/richhtml'
7 | import Banner from '../../components/banner'
8 | import appConfig from '../../config'
9 | import Utils from '../../utils'
10 | import Api from '../../api'
11 | import './index.scss'
12 |
13 | export default class AboutPage extends Component {
14 |
15 | state = {
16 | swiper: [],
17 | fetched: !1,
18 | page: {}
19 | }
20 |
21 | componentDidMount() {
22 | Api.page({
23 | page: 'about',
24 | pageId: appConfig.aboutId,
25 | tags: JSON.stringify({
26 | swiper: {
27 | tag: 'posts',
28 | args: {
29 | row: 3,
30 | flag: 'f'
31 | }
32 | },
33 | })
34 | }).then(res => {
35 | let page = res.data.page
36 | page.formatDate = Utils.dateFormat(page.addTime)
37 | page.dateTime = Utils.dateTimeFormat(page.addTime)
38 | this.setState({
39 | swiper: res.data.swiper.data || [],
40 | fetched: !0,
41 | page: page
42 | })
43 | Taro.setNavigationBarTitle({
44 | title: page.title
45 | })
46 | if (Taro.ENV_TYPE.SWAN === Taro.getEnv()) {
47 | swan.setPageInfo({
48 | title: page.seoTitle || page.title,
49 | keywords: page.keywords || '',
50 | description: page.description || '',
51 | articleTitle: page.title,
52 | release_date: page.dateTime,
53 | // 单张图时值可以是字符串
54 | image: page.logo,
55 | success: function () {
56 | console.log('页面基础信息设置完成');
57 | }
58 | })
59 | }
60 | }).catch(err => {
61 | Taro.showToast({
62 | icon: 'none',
63 | title: '获取页面信息失败'
64 | })
65 | })
66 | }
67 |
68 | render() {
69 | const { page, fetched, swiper } = this.state
70 | return (
71 |
72 |
73 | {page.logo && }
74 | {!page.logo && }
75 |
76 | {fetched ?
77 |
78 | :
79 |
82 | }
83 |
84 |
85 |
86 | )
87 | }
88 | }
89 |
90 |
--------------------------------------------------------------------------------
/src/pages/article/index.config.ts:
--------------------------------------------------------------------------------
1 | export default definePageConfig({
2 | navigationBarTitleText: '文章详情'
3 | })
4 |
--------------------------------------------------------------------------------
/src/pages/article/index.scss:
--------------------------------------------------------------------------------
1 | @import '../../var.scss';
--------------------------------------------------------------------------------
/src/pages/article/index.tsx:
--------------------------------------------------------------------------------
1 | import { Component } from 'react'
2 | import Taro from '@tarojs/taro'
3 | import { View, Text } from '@tarojs/components'
4 | import { AtLoadMore } from 'taro-ui'
5 | import Container from '../../components/container'
6 | import RichHtml from '../../components/richhtml'
7 | import Article from '../../components/article'
8 | import Utils from '../../utils'
9 | import Api from '../../api'
10 | import './index.scss'
11 | import utils from '../../utils'
12 | import { getCurrentInstance } from '@tarojs/runtime'
13 |
14 | export default class ArticlePage extends Component {
15 |
16 | state = {
17 | fetched: !1,
18 | article: {},
19 | related: [],
20 | }
21 |
22 | id = 0
23 |
24 | componentWillMount() {
25 | let params: any = getCurrentInstance().router?.params;
26 | this.id = params.id
27 | }
28 |
29 | componentDidMount() {
30 | Api.article({
31 | id: this.id,
32 | tags: JSON.stringify({
33 | related: {
34 | tag: 'articles',
35 | args: {
36 | row: 6,
37 | id: this.id,
38 | orderby: 'near',
39 | }
40 | }
41 | })
42 | }).then((res) => {
43 | let article = res.data.article
44 | article.formatDate = Utils.dateFormat(article.addTime)
45 | article.dateTime = Utils.dateTimeFormat(article.addTime)
46 | this.setState({
47 | fetched: !0,
48 | article: article,
49 | related: res.data.related.data
50 | })
51 | Taro.setNavigationBarTitle({
52 | title: article.title
53 | })
54 | if (Taro.ENV_TYPE.SWAN === Taro.getEnv()) {
55 | swan.setPageInfo({
56 | title: article.seoTitle || article.title,
57 | keywords: article.keywords || '',
58 | description: article.description || '',
59 | articleTitle: article.title,
60 | release_date: article.dateTime,
61 | // 单张图时值可以是字符串
62 | image: article.logo,
63 | success: function () {
64 | console.log('文章基础信息设置完成');
65 | }
66 | })
67 | }
68 | }).catch(res => {
69 | Taro.showToast({
70 | icon: 'none',
71 | title: '页面内容加载失败'
72 | })
73 | })
74 | }
75 |
76 | gotoCategory = (e) => {
77 | utils.navigate({
78 | url: '/pages/category/index?id=' + e
79 | })
80 | }
81 |
82 | gotoArticle = (e) => {
83 | utils.navigate({
84 | url: '/pages/article/index?id=' + e
85 | })
86 | }
87 |
88 | render() {
89 | const { article, fetched, related } = this.state
90 | return (
91 |
92 |
93 |
94 | {article.title}
95 |
96 | {article.category && {article.category.title}}
100 | {article.author && {article.author}}
103 | {article.formatDate}
106 |
107 |
108 |
109 | {fetched ?
110 |
111 | :
112 |
115 | }
116 |
117 |
118 | {article.prev && 上一篇:{article.prev.title}}
119 | {article.next && 下一篇:{article.next.title}}
120 |
121 |
122 | 相关文章
123 |
124 | {related.length > 0 && }
125 |
126 |
127 |
128 |
129 | )
130 | }
131 | }
132 |
133 |
--------------------------------------------------------------------------------
/src/pages/articles/index.config.ts:
--------------------------------------------------------------------------------
1 | export default definePageConfig({
2 | navigationBarTitleText: '新闻中心'
3 | })
4 |
--------------------------------------------------------------------------------
/src/pages/articles/index.scss:
--------------------------------------------------------------------------------
1 | .fixed-top{
2 | position: fixed;
3 | top: 0;
4 | width: 100%;
5 | z-index: 5;
6 | }
--------------------------------------------------------------------------------
/src/pages/articles/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import Taro from '@tarojs/taro'
3 | import { AtTabs } from 'taro-ui'
4 | import Container from '../../components/container'
5 | import Article from '../../components/article'
6 | import Banner from '../../components/banner'
7 | import appConfig from '../../config'
8 | import Api from '../../api'
9 | import './index.scss'
10 |
11 | export default class ArticlesPage extends Component {
12 |
13 | articleList = []
14 |
15 | state = {
16 | swiper: [],
17 | categories: [],
18 | currentId: 0,
19 | currentList: {
20 | page: 1,
21 | loading: !1,
22 | finished: !1,
23 | list: [],
24 | },
25 | pageConfig: {},
26 | fixed: !1,
27 | }
28 |
29 | componentWillMount() { }
30 |
31 | componentDidMount() {
32 | Api.articles({
33 | tags: JSON.stringify({
34 | categories: {
35 | tag: 'categories',
36 | args: {
37 | model: 'article',
38 | ignoreCategory: appConfig.ignoreCategory
39 | }
40 | },
41 | swiper: {
42 | tag: 'articles',
43 | args: {
44 | row: 3,
45 | flag: 'a'
46 | }
47 | },
48 | })
49 | }).then(res => {
50 | let pageConfig = res.data.pageConfig || {};
51 | this.setState({
52 | swiper: res.data.swiper.data || [],
53 | categories: res.data.categories || [],
54 | pageConfig: pageConfig
55 | }, () => {
56 | this.getTabArticles(0)
57 | })
58 | if (Taro.ENV_TYPE.SWAN === Taro.getEnv()) {
59 | swan.setPageInfo({
60 | title: pageConfig.seoTitle,
61 | keywords: pageConfig.keywords,
62 | description: pageConfig.description,
63 | image: pageConfig.logo,
64 | success: function () {
65 | console.log('新闻中心关键词配置成功');
66 | }
67 | })
68 | }
69 | }).catch(err => {
70 | Taro.showToast({
71 | icon: 'none',
72 | title: err.msg || err.message
73 | })
74 | })
75 | }
76 |
77 | clickTab = (e) => {
78 | if (e == this.state.currentId) {
79 | return
80 | }
81 | this.setState({
82 | currentId: e
83 | }, () => {
84 | this.getTabArticles(e)
85 | });
86 | }
87 |
88 | onPageScroll(e) {
89 | let top = e.scrollTop
90 | if (top > 150) {
91 | this.setState({
92 | fixed: !0
93 | })
94 | } else {
95 | this.setState({
96 | fixed: !1,
97 | })
98 | }
99 | }
100 |
101 | getTabArticles = (currentId) => {
102 | let currentCategory = this.state.categories[currentId]
103 | let currentList = this.articleList[currentId] || { list: [], page: 1 }
104 | if (currentList.loading || currentList.finished) {
105 | this.setState({
106 | currentList: currentList,
107 | })
108 | return
109 | }
110 | currentList.loading = !0
111 | this.setState({
112 | currentList: currentList,
113 | })
114 | Api.list({
115 | page: currentList.page,
116 | row: 10,
117 | categoryId: currentCategory.id
118 | }).then(res => {
119 | let articles = res.data || []
120 | currentList.page++
121 | currentList.loading = !1
122 | currentList.finished = !res.hasNext
123 | currentList.count = res.count
124 | currentList.list = currentList.list.concat(articles)
125 | this.articleList[currentId] = currentList
126 | this.setState({
127 | currentList: currentList
128 | })
129 | })
130 | }
131 |
132 | onReachBottom() {
133 | let { currentId } = this.state
134 | this.getTabArticles(currentId)
135 | }
136 |
137 | gotoArticle = (e) => {
138 | Taro.navigateTo({
139 | url: '/pages/article/index?id=' + e
140 | })
141 | }
142 |
143 | render() {
144 | const { currentId, currentList, categories, swiper, fixed } = this.state
145 | return (
146 |
147 |
148 | {categories.length > 0 &&
149 | }
150 | {currentList.list.length > 0 && }
151 |
152 | )
153 | } a
154 | }
155 |
--------------------------------------------------------------------------------
/src/pages/browser/index.config.ts:
--------------------------------------------------------------------------------
1 | export default definePageConfig({
2 | navigationBarTitleText: '网页'
3 | })
4 |
--------------------------------------------------------------------------------
/src/pages/browser/index.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/src/pages/browser/index.scss
--------------------------------------------------------------------------------
/src/pages/browser/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import Taro from '@tarojs/taro'
3 | import { WebView } from '@tarojs/components'
4 | import './index.scss'
5 | import { getCurrentInstance } from '@tarojs/runtime'
6 |
7 | export default class BrowserPage extends Component {
8 |
9 | state = {
10 | url: ''
11 | }
12 |
13 | componentWillMount () {
14 | let params: any = getCurrentInstance().router?.params;
15 | this.setState({
16 | url: params.url
17 | })
18 | }
19 |
20 | loadError() {
21 | Taro.showToast({
22 | icon: "none",
23 | title: '页面加载失败',
24 | })
25 | }
26 |
27 | loadSuccess(e) {
28 | console.log(e)
29 | }
30 |
31 | render () {
32 | const { url } = this.state
33 | return (
34 |
35 | )
36 | }
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/src/pages/category/index.config.ts:
--------------------------------------------------------------------------------
1 | export default definePageConfig({
2 | navigationBarTitleText: '分类详情'
3 | })
4 |
--------------------------------------------------------------------------------
/src/pages/category/index.scss:
--------------------------------------------------------------------------------
1 | @import '../../var.scss';
2 | .sub-categories{
3 | color: $primary;
4 | padding-bottom: 20px;
5 | .sub-category-item{
6 | color: $primary;
7 | border: 1px solid $primary;
8 | text-align:center;
9 | margin:10px;
10 | border-radius:6px;
11 | padding:8px;
12 | }
13 | }
--------------------------------------------------------------------------------
/src/pages/category/index.tsx:
--------------------------------------------------------------------------------
1 | import { Component } from 'react'
2 | import Taro from '@tarojs/taro'
3 | import { View, Text, Image } from '@tarojs/components'
4 | import { AtDivider, AtLoadMore } from 'taro-ui'
5 | import Container from '../../components/container'
6 | import RichHtml from '../../components/richhtml'
7 | import Product from '../../components/product'
8 | import Article from '../../components/article'
9 | import Utils from '../../utils'
10 | import Api from '../../api'
11 | import './index.scss'
12 | import utils from '../../utils'
13 | import { getCurrentInstance } from '@tarojs/runtime'
14 |
15 | export default class CategoryPage extends Component {
16 |
17 | id = 0
18 | page = 1
19 |
20 | state = {
21 | category: {},
22 | list: [],
23 | count: 0,
24 | fetched: !1,
25 | loading: !1,
26 | finished: !1,
27 | }
28 |
29 | componentWillMount() {
30 | let params: any = getCurrentInstance().router?.params;
31 | this.id = params.id
32 | }
33 |
34 | componentDidMount() {
35 | Api.category({
36 | id: this.id,
37 | child: true
38 | }).then(res => {
39 | let category = res.data.category
40 | category.formatDate = Utils.dateFormat(category.addTime)
41 | category.dateTime = Utils.dateTimeFormat(category.addTime)
42 | this.setState({
43 | fetched: !0,
44 | category: category
45 | })
46 | Taro.setNavigationBarTitle({
47 | title: category.title
48 | })
49 | if (Taro.ENV_TYPE.SWAN === Taro.getEnv()) {
50 | swan.setPageInfo({
51 | title: category.seoTitle || category.title,
52 | keywords: category.keywords || '',
53 | description: category.description || '',
54 | articleTitle: category.title,
55 | release_date: category.dateTime,
56 | // 单张图时值可以是字符串
57 | image: category.logo,
58 | success: function () {
59 | console.log('分类基础信息设置完成');
60 | }
61 | })
62 | }
63 | }).catch(err => {
64 | Taro.showToast({
65 | icon: 'none',
66 | title: '获取分类信息失败'
67 | })
68 | })
69 | this.page = 1
70 | this.setState({
71 | list: [],
72 | loading: !1,
73 | finished: !1
74 | }, () => {
75 | this.getDatas()
76 | })
77 | }
78 |
79 | onPullDownRefresh() {
80 | this.page = 1
81 | this.setState({
82 | list: [],
83 | loading: !1,
84 | finished: !1
85 | }, () => {
86 | this.getDatas()
87 | })
88 | }
89 |
90 | onReachBottom() {
91 | this.getDatas()
92 | }
93 |
94 | getDatas() {
95 | let { list, finished, loading } = this.state
96 | if (finished || loading) {
97 | return
98 | }
99 | this.setState({
100 | loading: !0,
101 | })
102 | Api.list({
103 | page: this.page,
104 | row: 10,
105 | categoryId: this.id
106 | }).then(res => {
107 | Taro.stopPullDownRefresh()
108 | this.setState({
109 | list: list.concat(res.data || []),
110 | count: res.count,
111 | finished: !res.hasNext,
112 | loading: !1,
113 | })
114 | this.page++
115 | })
116 | }
117 |
118 | changeCategory = (e) => {
119 | utils.navigate({
120 | url: '/pages/category/index?id=' + e
121 | })
122 | }
123 |
124 | render() {
125 | const { list, count, category, fetched, finished, loading } = this.state
126 | return (
127 |
128 |
129 | {category.logo && }
130 | {fetched ?
131 |
132 | {category.title}
133 |
134 | {category.children &&
135 | {category.children.map((item, index) => {
136 | return
137 | {item.title}
138 |
139 | })}
140 | }
141 | 共{count}篇{category.model == 'product' ? '产品' : '文章'}
142 |
143 |
144 | :
145 |
148 | }
149 | {category.nodes && category.nodes.length &&
150 |
151 | }
152 |
153 | {category.model == 'product' && }
154 | {category.model != 'product' && }
155 | {finished && }
156 | {loading && }
157 | {(!loading && !finished) && }
158 |
159 | )
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/src/pages/contact/index.config.ts:
--------------------------------------------------------------------------------
1 | export default definePageConfig({
2 | navigationBarTitleText: '联系我们'
3 | })
4 |
--------------------------------------------------------------------------------
/src/pages/contact/index.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fesiong/dedeapp/bfae2b285d483aa6f525f2576ca12b1f56a5edd4/src/pages/contact/index.scss
--------------------------------------------------------------------------------
/src/pages/contact/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import Taro from '@tarojs/taro'
3 | import { View, Image, Text } from '@tarojs/components'
4 | import { AtLoadMore } from 'taro-ui'
5 | import Container from '../../components/container'
6 | import RichHtml from '../../components/richhtml'
7 | import Banner from '../../components/banner'
8 | import Cell from '../../components/cell'
9 | import appConfig from '../../config'
10 | import Utils from '../../utils'
11 | import Api from '../../api'
12 | import './index.scss'
13 |
14 | export default class AboutPage extends Component {
15 |
16 | state = {
17 | swiper: [],
18 | setting: {
19 | mobile: appConfig.mobile,
20 | company: appConfig.company,
21 | address: appConfig.address,
22 | wechat: appConfig.wechat,
23 | },
24 | fetched: !1,
25 | page: {}
26 | }
27 |
28 | componentDidMount () {
29 | let { setting } = this.state
30 | Api.page({
31 | page: 'contact',
32 | pageId: appConfig.contactId,
33 | tags: JSON.stringify({
34 | swiper: {
35 | tag: 'posts',
36 | args: {
37 | row: 3,
38 | flag: 'f'
39 | }
40 | },
41 | })
42 | }).then(res => {
43 | let page = res.data.page
44 | page.formatDate = Utils.dateFormat(page.addTime)
45 | page.dateTime = Utils.dateTimeFormat(page.addTime)
46 | this.setState({
47 | swiper: res.data.swiper.data || [],
48 | fetched: !0,
49 | page: page
50 | })
51 | Taro.setNavigationBarTitle({
52 | title: page.title
53 | })
54 | if(Taro.ENV_TYPE.SWAN === Taro.getEnv()){
55 | swan.setPageInfo({
56 | title: page.seoTitle || page.title,
57 | keywords: page.keywords || '',
58 | description: page.description || '',
59 | articleTitle: page.title,
60 | release_date: page.dateTime,
61 | // 单张图时值可以是字符串
62 | image: page.logo,
63 | success: function () {
64 | console.log('页面基础信息设置完成');
65 | }
66 | })
67 | }
68 | }).catch(err => {
69 | Taro.showToast({
70 | icon: 'none',
71 | title: '获取页面信息失败'
72 | })
73 | })
74 |
75 | Api.setting().then(res => {
76 | this.setState({
77 | setting: Object.assign(setting, res.data.setting)
78 | })
79 | }).catch(err => {
80 | Taro.showToast({
81 | icon: 'none',
82 | title: err.msg || err.message
83 | })
84 | })
85 | }
86 |
87 | phoneCall = () => {
88 | Taro.makePhoneCall({
89 | phoneNumber: this.state.setting.mobile
90 | })
91 | }
92 |
93 | copyWechat = () => {
94 | Taro.setClipboardData({
95 | data: this.state.setting.wechat
96 | }).then(res => {
97 | Taro.showToast({
98 | icon: 'none',
99 | title: '微信已复制'
100 | })
101 | })
102 | }
103 |
104 | render () {
105 | const { page, fetched, setting, swiper } = this.state
106 | return (
107 |
108 |
109 | {page.logo && }
110 | {!page.logo && }
111 |
112 | {fetched ?
113 |
114 | :
115 |
118 | }
119 |
120 |
121 |
122 | 联系我们
123 |
124 | {setting.company && | }
125 | {setting.address && | }
126 | {setting.mobile && }
127 | |
128 |
129 |
130 | )
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/src/pages/index/index.config.ts:
--------------------------------------------------------------------------------
1 | export default definePageConfig({
2 | navigationBarTitleText: "首页"
3 | })
4 |
--------------------------------------------------------------------------------
/src/pages/index/index.scss:
--------------------------------------------------------------------------------
1 | @import '../../var.scss';
2 | .index-grid{
3 | background: #fff;
4 | padding: 20px 0;
5 | .grid-item{
6 | text-align: center;
7 | .grid-image{
8 | padding: 10px;
9 | box-sizing: border-box;
10 | width: 120px;
11 | height: 120px;
12 | }
13 | .grid-text{
14 | color: $secondary;
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/pages/index/index.tsx:
--------------------------------------------------------------------------------
1 | import { Component } from 'react'
2 | import Taro from '@tarojs/taro'
3 | import { View, Image, Block, Text } from '@tarojs/components'
4 | import Container from '../../components/container'
5 | import Product from '../../components/product'
6 | import Article from '../../components/article'
7 | import Banner from '../../components/banner'
8 | import appConfig from '../../config'
9 | import Api from '../../api'
10 | import './index.scss'
11 |
12 | export default class IndexPage extends Component {
13 |
14 | state = {
15 | nav: appConfig.indexNav || [],
16 | swiper: appConfig.indexBanner || [],
17 | pageConfig: {},
18 | categories: [],
19 | recommendArticles: [],
20 | recommendProducts: [],
21 | showProduct: appConfig.showProduct
22 | }
23 |
24 | componentDidMount() {
25 | if (Taro.ENV_TYPE.SWAN === Taro.getEnv()) {
26 | swan.setPageInfo({
27 | title: appConfig.seoTitle,
28 | keywords: appConfig.keywords,
29 | description: appConfig.description,
30 | image: appConfig.logo,
31 | success: function () {
32 | console.log('首页关键词首次配置成功');
33 | }
34 | })
35 | }
36 |
37 | let tags = {
38 | swiper: {
39 | tag: 'posts',
40 | args: {
41 | row: 3,
42 | flag: 'f'
43 | }
44 | },
45 | recommendArticles: {
46 | tag: 'articles',
47 | args: {
48 | row: 4,
49 | flag: 'c'
50 | }
51 | },
52 | }
53 | if(this.state.showProduct){
54 | tags['recommendProducts'] = {
55 | tag: 'products',
56 | args: {
57 | row: 4,
58 | flag: 'c'
59 | }
60 | }
61 | }
62 |
63 | Api.index({
64 | tags: JSON.stringify(tags)
65 | }).then(res => {
66 | let pageConfig = res.data.pageConfig || {}
67 | let recommendProducts = res.data.recommendProducts && res.data.recommendProducts.data || []
68 | let recommendArticles = res.data.recommendArticles.data || []
69 | let swiper = res.data.swiper && res.data.swiper.data || []
70 | if(swiper.length){
71 | this.setState({
72 | swiper: swiper
73 | })
74 | }
75 | Taro.setNavigationBarTitle({
76 | title: pageConfig.seoTitle
77 | })
78 | this.setState({
79 | recommendProducts: recommendProducts,
80 | recommendArticles: recommendArticles,
81 | pageConfig: pageConfig
82 | })
83 | if (Taro.ENV_TYPE.SWAN === Taro.getEnv()) {
84 | swan.setPageInfo({
85 | title: pageConfig.seoTitle,
86 | keywords: pageConfig.keywords,
87 | description: pageConfig.description,
88 | image: pageConfig.logo,
89 | success: function () {
90 | console.log('首页关键词配置成功');
91 | }
92 | })
93 | }
94 | }).catch(err => {
95 | Taro.showToast({
96 | icon: 'none',
97 | title: err.msg || err.message
98 | })
99 | })
100 |
101 | this.loadIndexCategories()
102 | }
103 |
104 | loadIndexCategories() {
105 | Api.default({
106 | tags: JSON.stringify({
107 | categories: {
108 | tag: 'categories',
109 | args: {
110 | row: 10,
111 | categoryId: appConfig.indexCategory
112 | },
113 | children: {
114 | posts: {
115 | tag: 'posts',
116 | args: {
117 | row: 10,
118 | categoryId: 'parent:id'
119 | },
120 | }
121 | }
122 | }
123 | })
124 | }).then(res => {
125 | //console.log(res);
126 | let categories = res.data.categories || []
127 | this.setState({
128 | categories: categories,
129 | })
130 | }).catch(err => {
131 | Taro.showToast({
132 | icon: 'none',
133 | title: err.msg || err.message
134 | })
135 | })
136 | }
137 |
138 | gotoProduct = (e) => {
139 | Taro.navigateTo({
140 | url: '/pages/product/index?id=' + e
141 | })
142 | }
143 |
144 | gotoArticle = (e) => {
145 | Taro.navigateTo({
146 | url: '/pages/article/index?id=' + e
147 | })
148 | }
149 |
150 | handleNavClick = (e) => {
151 | Taro.switchTab({
152 | url: e
153 | }).catch(res => {
154 | Taro.navigateTo({
155 | url: e
156 | })
157 | });
158 | }
159 |
160 | render() {
161 | const { swiper, nav, showProduct, recommendProducts, recommendArticles, categories } = this.state
162 | return (
163 |
164 |
165 |
166 | {nav.map((item, index) => {
167 | return
168 |
169 |
170 | {item.title}
171 |
172 |
173 | })
174 | }
175 |
176 | {(!!showProduct && recommendProducts.length > 0) &&
177 | 推荐产品
178 |
179 |
180 |
181 | }
182 | {recommendArticles.length > 0 &&
183 | 热门文章
184 |
185 |
186 |
187 | }
188 | {categories.length > 0 &&
189 | {categories.map((category: any) => {
190 | return
191 | {category.title}
192 |
193 | {category.model == 'product' && }
194 | {category.model == 'article' && }
195 |
196 |
197 | })}
198 | }
199 |
200 | )
201 | }
202 | }
203 |
--------------------------------------------------------------------------------
/src/pages/product/index.config.ts:
--------------------------------------------------------------------------------
1 | export default definePageConfig({
2 | navigationBarTitleText: '产品详情',
3 | transparentTitle: "auto",
4 | })
5 |
--------------------------------------------------------------------------------
/src/pages/product/index.scss:
--------------------------------------------------------------------------------
1 | @import '../../var.scss';
2 |
--------------------------------------------------------------------------------
/src/pages/product/index.tsx:
--------------------------------------------------------------------------------
1 | import { Component } from 'react'
2 | import Taro, { getApp } from '@tarojs/taro'
3 | import { View, Image, Text } from '@tarojs/components'
4 | import { AtLoadMore } from 'taro-ui'
5 | import Container from '../../components/container'
6 | import RichHtml from '../../components/richhtml'
7 | import Product from '../../components/product'
8 | import Utils from '../../utils'
9 | import Api from '../../api'
10 | import './index.scss'
11 | import utils from '../../utils'
12 | import { getCurrentInstance } from '@tarojs/runtime'
13 |
14 | export default class ProductPage extends Component {
15 |
16 | state = {
17 | fetched: !1,
18 | product: {},
19 | related: [],
20 | }
21 |
22 | id = 0
23 |
24 | componentWillMount() {
25 | let params: any = getCurrentInstance().router?.params;
26 | this.id = params.id
27 | }
28 |
29 | componentDidMount() {
30 | Api.product({
31 | id: this.id,
32 | tags: JSON.stringify({
33 | related: {
34 | tag: 'products',
35 | args: {
36 | row: 6,
37 | id: this.id,
38 | orderby: 'near',
39 | }
40 | }
41 | })
42 | }).then((res) => {
43 | let product = res.data.product
44 | product.formatDate = Utils.dateFormat(product.addTime)
45 | product.dateTime = Utils.dateTimeFormat(product.addTime)
46 | this.setState({
47 | fetched: !0,
48 | product: product,
49 | related: res.data.related.data
50 | })
51 | Taro.setNavigationBarTitle({
52 | title: product.title
53 | })
54 | if (Taro.ENV_TYPE.SWAN === Taro.getEnv()) {
55 | swan.setPageInfo({
56 | title: product.seoTitle || product.title,
57 | keywords: product.keywords || '',
58 | description: product.description || '',
59 | articleTitle: product.title,
60 | release_date: product.dateTime,
61 | // 单张图时值可以是字符串
62 | image: product.logo,
63 | success: function () {
64 | console.log('产品基础信息设置完成');
65 | }
66 | })
67 | }
68 | }).catch(res => {
69 | console.log(res)
70 | Taro.showToast({
71 | icon: 'none',
72 | title: '页面内容加载失败'
73 | })
74 | })
75 | }
76 |
77 | gotoCategory = (e) => {
78 | utils.navigate({
79 | url: '/pages/category/index?id=' + e
80 | })
81 | }
82 |
83 | gotoProduct = (e) => {
84 | utils.navigate({
85 | url: '/pages/product/index?id=' + e
86 | })
87 | }
88 |
89 | render() {
90 | const { product, fetched, related } = this.state
91 | return (
92 |
93 |
94 | {product.logo && }
95 |
96 | {product.title}
97 |
98 | {product.category && {product.category.title}}
102 | {product.author && {product.author}}
105 | {product.formatDate}
108 |
109 |
110 |
111 | 产品详情
112 |
113 |
114 | {fetched ?
115 |
116 | :
117 |
120 | }
121 |
122 |
123 | {product.prev && 上一产品:{product.prev.title}}
124 | {product.next && 下一产品:{product.next.title}}
125 |
126 |
127 | 相关产品
128 |
129 | {related.length && }
130 |
131 |
132 |
133 |
134 | )
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/src/pages/products/index.config.ts:
--------------------------------------------------------------------------------
1 | export default definePageConfig({
2 | navigationBarTitleText: '产品中心'
3 | })
4 |
--------------------------------------------------------------------------------
/src/pages/products/index.scss:
--------------------------------------------------------------------------------
1 | .fixed-top{
2 | position: fixed;
3 | top: 0;
4 | width: 100%;
5 | z-index: 5;
6 | }
--------------------------------------------------------------------------------
/src/pages/products/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import Taro from '@tarojs/taro'
3 | import { AtTabs } from 'taro-ui'
4 | import Container from '../../components/container'
5 | import Product from '../../components/product'
6 | import Banner from '../../components/banner'
7 | import appConfig from '../../config'
8 | import Api from '../../api'
9 | import './index.scss'
10 |
11 | export default class ProductsPage extends Component {
12 |
13 | productList = []
14 |
15 | state = {
16 | swiper: [],
17 | categories: [],
18 | currentId: 0,
19 | currentList: {
20 | page: 1,
21 | loading: !1,
22 | finished: !1,
23 | list: [],
24 | },
25 | pageConfig: {},
26 | fixed: !1,
27 | }
28 |
29 | componentWillMount() { }
30 |
31 | componentDidMount() {
32 | Api.products({
33 | tags: JSON.stringify({
34 | categories: {
35 | tag: 'categories',
36 | args: {
37 | ignoreCategory: appConfig.ignoreCategory,
38 | categoryId: appConfig.productCategory,
39 | model: 'product'
40 | }
41 | },
42 | swiper: {
43 | tag: 'articles',
44 | args: {
45 | row: 3,
46 | flag: 'a'
47 | }
48 | },
49 | })
50 | }).then(res => {
51 | let pageConfig = res.data.pageConfig || {};
52 | this.setState({
53 | swiper: res.data.swiper.data || [],
54 | categories: res.data.categories || [],
55 | pageConfig: pageConfig
56 | }, () => {
57 | this.getTabProducts(0)
58 | })
59 | if (Taro.ENV_TYPE.SWAN === Taro.getEnv()) {
60 | swan.setPageInfo({
61 | title: pageConfig.seoTitle,
62 | keywords: pageConfig.keywords,
63 | description: pageConfig.description,
64 | image: pageConfig.logo,
65 | success: function () {
66 | console.log('产品中心关键词配置成功');
67 | }
68 | })
69 | }
70 | }).catch(err => {
71 | Taro.showToast({
72 | icon: 'none',
73 | title: err.msg || err.message
74 | })
75 | })
76 | }
77 |
78 | clickTab = (e) => {
79 | if (e == this.state.currentId) {
80 | return
81 | }
82 | this.setState({
83 | currentId: e
84 | }, () => {
85 | this.getTabProducts(e)
86 | });
87 | }
88 |
89 | onPageScroll(e) {
90 | let top = e.scrollTop
91 | if (top > 150) {
92 | this.setState({
93 | fixed: !0
94 | })
95 | } else {
96 | this.setState({
97 | fixed: !1,
98 | })
99 | }
100 | }
101 |
102 | getTabProducts = (currentId) => {
103 | let currentCategory = this.state.categories[currentId]
104 | let currentList = this.productList[currentId] || { list: [], page: 1 }
105 | if (currentList.loading || currentList.finished) {
106 | this.setState({
107 | currentList: currentList,
108 | })
109 | return
110 | }
111 | currentList.loading = !0
112 | this.setState({
113 | currentList: currentList,
114 | })
115 | Api.list({
116 | page: currentList.page,
117 | row: 10,
118 | categoryId: currentCategory.id,
119 | // order: "desc",
120 | // sudDay: 7,
121 | // keyword: "指定关键词",
122 | // flag: "c,f",
123 | // ids: "1,2,3",
124 | // orderby: "hot"//hot|click|sort|id|near|last|rand
125 | }).then(res => {
126 | let products = res.data || []
127 | currentList.page++
128 | currentList.loading = !1
129 | currentList.finished = !res.hasNext
130 | currentList.count = res.count
131 | currentList.list = currentList.list.concat(products)
132 | this.productList[currentId] = currentList
133 | this.setState({
134 | currentList: currentList
135 | })
136 | })
137 | }
138 |
139 | onReachBottom() {
140 | let { currentId } = this.state
141 | this.getTabProducts(currentId)
142 | }
143 |
144 | gotoProduct = (e) => {
145 | Taro.navigateTo({
146 | url: '/pages/product/index?id=' + e
147 | })
148 | }
149 |
150 | render() {
151 | const { currentId, currentList, categories, swiper, fixed } = this.state
152 | return (
153 |
154 |
155 | {categories.length > 0 &&
156 | }
157 | {currentList.list.length > 0 && }
158 |
159 | )
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/src/project.swan.json:
--------------------------------------------------------------------------------
1 | {
2 | "appid": "16420765",
3 | "editor": {
4 |
5 | },
6 | "host": "baiduboxapp",
7 | "publish": {
8 | "version": "0.5.0"
9 | },
10 | "setting": {
11 | "urlCheck": false
12 | },
13 | "swan": {
14 | "baiduboxapp": {
15 | "swanJsVersion": "3.60.20",
16 | "extensionJsVersion": "1.1.1"
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/src/request.ts:
--------------------------------------------------------------------------------
1 | import Taro from '@tarojs/taro'
2 | import appConfig from './config'
3 | import Version from './version'
4 | import store from './store'
5 |
6 | function crc32(a) {
7 | let c = function() {
8 | for (var d = 0, f = new Array(256), g = 0; 256 != g; ++g) {
9 | d = g, d = 1 & d ? -306674912 ^ d >>> 1 : d >>> 1, d = 1 & d ? -306674912 ^ d >>> 1 : d >>> 1, d = 1 & d ? -306674912 ^ d >>> 1 : d >>> 1, d = 1 & d ? -306674912 ^ d >>> 1 : d >>> 1, d = 1 & d ? -306674912 ^ d >>> 1 : d >>> 1, d = 1 & d ? -306674912 ^ d >>> 1 : d >>> 1, d = 1 & d ? -306674912 ^ d >>> 1 : d >>> 1, d = 1 & d ? -306674912 ^ d >>> 1 : d >>> 1, f[g] = d
10 | }
11 | return "undefined" != typeof Int32Array ? new Int32Array(f) : f
12 | }(),
13 | b = function(g) {
14 | for (var j, k, h = -1, f = 0, d = g.length; f < d;) {
15 | j = g.charCodeAt(f++),j < 128 ? h = h >>> 8 ^ c[255 & (h ^ j)] : j < 2048 ? (h = h >>> 8 ^ c[255 & (h ^ (192 | j >> 6 & 31))], h = h >>> 8 ^ c[255 & (h ^ (128 | 63 & j))]) : j >= 55296 && j < 57344 ? (j = (1023 & j) + 64, k = 1023 & g.charCodeAt(f++), h = h >>> 8 ^ c[255 & (h ^ (240 | j >> 8 & 7))], h = h >>> 8 ^ c[255 & (h ^ (128 | j >> 2 & 63))], h = h >>> 8 ^ c[255 & (h ^ (128 | k >> 6 & 15 | (3 & j) << 4))], h = h >>> 8 ^ c[255 & (h ^ (128 | 63 & k))]) : (h = h >>> 8 ^ c[255 & (h ^ (224 | j >> 12 & 15))], h = h >>> 8 ^ c[255 & (h ^ (128 | j >> 6 & 63))], h = h >>> 8 ^ c[255 & (h ^ (128 | 63 & j))])
16 | }
17 | return h ^ -1
18 | };
19 | return String(b(a) >>> 0)
20 | }
21 |
22 | function request(url, params, cacheKey = ''){
23 | url = appConfig.api + url
24 | let header = {
25 | 'X-Requested-With': 'XMLHttpRequest',
26 | 'Version': Version
27 | }
28 |
29 | return Taro.request({
30 | url: url,
31 | data: params,
32 | header: header
33 | }).then(async (res) => {
34 | const { code } = res.data
35 | if (code !== 0) {
36 | return Promise.reject(res.data)
37 | }
38 |
39 | if(cacheKey){
40 | store.setCache(cacheKey, res.data)
41 | }
42 |
43 | return res.data
44 | }).catch((err) => {
45 | const defaultMsg = '请求异常'
46 | return Promise.reject({ message: defaultMsg, ...err })
47 | })
48 | }
49 |
50 | export default async function fetch(url, params: any = {}, cache = false) {
51 | params.ignoreCategory = appConfig.ignoreCategory
52 | params.productModelId = appConfig.productModelId
53 | params.productCategory = appConfig.productCategory
54 | params.showProduct = appConfig.showProduct
55 | if(cache){
56 | let cacheKey = crc32(url + JSON.stringify(params))
57 | let data = store.getCache(cacheKey)
58 | if(data){
59 | return data;
60 | }else{
61 | return request(url, params, cacheKey)
62 | }
63 | }else{
64 | return request(url, params)
65 | }
66 | }
--------------------------------------------------------------------------------
/src/store.ts:
--------------------------------------------------------------------------------
1 | var cacheData = {};
2 |
3 | export default {
4 | getCache: (t:string) => {
5 | return cacheData[t]
6 | },
7 |
8 | setCache: (t:string, a:any) => {
9 | cacheData[t] = a
10 | },
11 | }
--------------------------------------------------------------------------------
/src/theme.scss:
--------------------------------------------------------------------------------
1 | @import 'var.scss';
2 | /*主题*/
3 | .default-theme{
4 |
5 | }
6 | .network-theme{
7 | .panel-title{
8 | text-align: center;
9 | padding-left: 20px;
10 | border-bottom: none;
11 | color: $primary;
12 | .title-text{
13 | background: #fff;
14 | display: inline-block;
15 | padding: 0 30rpx;
16 | position:relative;
17 | z-index: 1;
18 | }
19 | &::before{
20 | content: " ";
21 | display: inline-block;
22 | position: absolute;
23 | height: 0px;
24 | border-bottom: 4px dashed $primary;
25 | background-color: transparent;
26 | margin-top: -2px;
27 | width: 60%;
28 | top: 50%;
29 | left: 20%;
30 | right: 20%;
31 | box-shadow: none;
32 | border-radius: 0;
33 | }
34 | }
35 | // .product-list{
36 | // background: #fff;
37 | // margin-bottom: 0;
38 | // .product-item{
39 | // .inner{
40 | // padding: 10px;
41 | // }
42 | // .product-image{
43 | // border: $border;
44 | // }
45 | // }
46 | // }
47 | // .article-list{
48 | // .article-item{
49 | // margin: 0;
50 | // border-bottom: $border;
51 | // &:last-child{
52 | // border-bottom: none;
53 | // }
54 | // }
55 | // }
56 | // .index-grid{
57 | // .grid-item{
58 | // .grid-text{
59 | // color: $secondary;
60 | // }
61 | // }
62 | // }
63 | }
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import Taro from "@tarojs/taro"
2 | import { getCurrentPages } from "@tarojs/taro"
3 |
4 | function padZero(str, num = 2){
5 | return String(str).padStart(num, "0")
6 | }
7 |
8 | const utils = {
9 | dateFormat: function(t, full = true){
10 | let date = new Date(t*1000)
11 | let dateStr = padZero(date.getMonth()+1) + "-" + padZero(date.getDate())
12 | if(full){
13 | dateStr = date.getFullYear() + "-" + dateStr
14 | }
15 |
16 | return dateStr;
17 | },
18 | dateTimeFormat: function(t, full = true){
19 | let date = new Date(t*1000)
20 | let timeStr = padZero(date.getHours()) + ":" + padZero(date.getMinutes()) + ":" + padZero(date.getSeconds())
21 | let dateStr = date.getFullYear() + "-" + padZero(date.getMonth()+1) + "-" + padZero(date.getDate())
22 |
23 | return dateStr + " " + timeStr
24 | },
25 | navigate: function (t) {
26 | (getCurrentPages() || []).length >= 5 ? Taro.redirectTo(t) : Taro.navigateTo(t)
27 | }
28 | }
29 |
30 | export default utils
31 |
--------------------------------------------------------------------------------
/src/var.scss:
--------------------------------------------------------------------------------
1 | $primary: #266AAE;
2 | $secondary: #666666;
3 | $third: #f7f9ff;
4 | $background: #f6f6f6;
5 | $shadow: 1px 0 6px rgba($primary, 0.2);
6 | $border: 2px solid rgba($primary, 0.1);
7 | // /*第二配色*/
8 | // $primary: #ff9800;
9 | // $secondary: #666666;
10 | // $third: #f7f9ff;
11 | // $background: #f6f6f6;
12 | // $shadow: 1px 0 6px rgba($primary, 0.2);
13 | // $border: 2px solid rgba($primary, 0.1);
--------------------------------------------------------------------------------
/src/version.ts:
--------------------------------------------------------------------------------
1 | const version = '1.0.0'
2 |
3 | export default version
--------------------------------------------------------------------------------
/src/使用帮助.url:
--------------------------------------------------------------------------------
1 | [InternetShortcut]
2 | URL=https://docs.qq.com/doc/DUmlzbGtVTFJPbUpz
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2017",
4 | "module": "commonjs",
5 | "removeComments": false,
6 | "preserveConstEnums": true,
7 | "moduleResolution": "node",
8 | "experimentalDecorators": true,
9 | "noImplicitAny": false,
10 | "allowSyntheticDefaultImports": true,
11 | "outDir": "lib",
12 | "noUnusedLocals": true,
13 | "noUnusedParameters": true,
14 | "strictNullChecks": true,
15 | "sourceMap": true,
16 | "baseUrl": ".",
17 | "rootDir": ".",
18 | "jsx": "react-jsx",
19 | "allowJs": true,
20 | "resolveJsonModule": true,
21 | "typeRoots": [
22 | "node_modules/@types"
23 | ],
24 | "paths": {
25 | "@/*": ["src/*"]
26 | }
27 | },
28 | "include": ["./src", "./types", "./config"],
29 | "compileOnSave": false
30 | }
31 |
--------------------------------------------------------------------------------