提交 182a5f5d 编写于 作者: 张磊's avatar 张磊

auto commit

上级
流水线 #52093 已取消 于阶段
# comment
deploy
node_modules
public
[*.{js,jsx,ts,tsx,vue,scss,css}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true
NODE_ENV=development
# baseUrl
BASE_URL=/pica-insurance/
# 路由 base
VUE_APP_BASE_ROUTE_URL=/pica-insurance
# 默认路由地址
VUE_APP_BASE_ROUTE=/
# 阿里云OSS对象存储地址
VUE_APP_OSS_URL=https://pica-h5-dev.yunqueyi.com/
# 模拟用户信息,如果有则发送模拟用户信息,只针对开发模式
# VUE_APP_MOCK_USER_INFO=eyJhdmF0YXIiOm51bGwsIm1vYmlsZSI6IjE4MTE3MDIzOTE2Iiwibmlja05hbWUiOiIxODEqKioqMzkxNiIsImlkZW50aWZ5U3RhdHVzIjpudWxsLCJ1c2VySWQiOiI2OTIzMDczNDM1ODQ2NjE1MDQiLCJsb2dpbkNoYW5uZWwiOiJtYW5pdWprLWNoYW5uZWwtY29uc3VtZXItYXBwIn0=
# 是否加密
VUE_APP_ENCRYPT=
# 是否开启 vConsole
VUE_APP_CONSOLE=true
# appid
VUE_APP_APPID=ab5cb3c1fd834ca1892cd7b3c6e57717
#生成骨架屏扫描地址(可选择本地)
VUE_APP_SKELETON='http://localhost:8083'
# 域名
VUE_APP_DOMAIN='phome.yunqueyi.com'
NODE_ENV=production
# 自定义 VUE_APP_ENV
VUE_APP_ENV ='dev'
VUE_APP_SERVICE_URL=https://dev-sc.yunqueyi.com
# 阿里云OSS对象存储地址
VUE_APP_OSS_URL=https://pica-h5-dev.yunqueyi.com/
# 模拟用户信息,如果有则发送模拟用户信息,只针对开发模式
VUE_APP_MOCK_USER_INFO=
# 是否加密
VUE_APP_ENCRYPT=
# 是否开启vConsole
VUE_APP_CONSOLE=true
# 是否开启打印 console.log
VUE_APP_HAS_CONSOLE=true
#OSS上传Bucket_Name
VUE_APP_BUCKET_NAME='pica-app-dev'
# appid
VUE_APP_APPID=wxf4e66242d31c81c2
# 本地化存储:0、关闭 1、打开
VUE_APP_CACHE_CONTROL=0
# 域名
VUE_APP_DOMAIN='dev-phome.yunqueyi.com'
NODE_ENV=development
VUE_APP_ENV=development
VUE_APP_SERVICE_URL=
# 模拟用户信息
VUE_APP_MOCK_USER_INFO=eyJhdmF0YXIiOm51bGwsIm1vYmlsZSI6IjE4MTE3MDIzOTE2Iiwibmlja05hbWUiOiIxODEqKioqMzkxNiIsImlkZW50aWZ5U3RhdHVzIjpudWxsLCJ1c2VySWQiOiI2OTIzMDczNDM1ODQ2NjE1MDQiLCJsb2dpbkNoYW5uZWwiOiJtYW5pdWprLWNoYW5uZWwtY29uc3VtZXItYXBwIn0=
VUE_APP_ENCRYPT=
VUE_APP_CONSOLE=true
# 本地开发环境
VUE_APP_IS_LOCAL=true
# appid
VUE_APP_APPID=wxf4e66242d31c81c2
# 本地token
VUE_APP_TOKEN=42789F2675384BFAB05EA57BDBED0E73
# mock的基础地址
VUE_APP_BASE_MOCK_URL=http://192.168.120.69:40001/mock/
# mock的工作空间地址
VUE_APP_MOCK_URL=12/
NODE_ENV=production
# 自定义 VUE_APP_ENV
VUE_APP_ENV ='production'
VUE_APP_SERVICE_URL=https://sc.yunqueyi.com
# 阿里云OSS对象存储地址
VUE_APP_OSS_URL=https://pica-h5.yunqueyi.com/
# 模拟用户信息,如果有则发送模拟用户信息,只针对开发模式
VUE_APP_MOCK_USER_INFO=
# 是否加密
VUE_APP_ENCRYPT=false
# 是否开启 vConsole
VUE_APP_CONSOLE=
# appid
VUE_APP_APPID=wx2c577552a2d28550
# app渠道
VUE_APP_CHANNEL=prod
# OSS 上传 Bucket_Name
VUE_APP_BUCKET_NAME='pica-app-prod'
#arms日志监控脚本
VUE_APP_ARMS=''
# 本地化存储:0、关闭 1、打开
VUE_APP_CACHE_CONTROL=0
# 域名
VUE_APP_DOMAIN='phome.yunqueyi.com'
NODE_ENV=production
# 自定义 VUE_APP_ENV
VUE_APP_ENV ='testing'
VUE_APP_SERVICE_URL=https://test1-sc.yunqueyi.com
# 阿里云OSS对象存储地址
VUE_APP_OSS_URL=https://pica-h5-test.yunqueyi.com/
# 模拟用户信息,如果有则发送模拟用户信息,只针对开发模式
VUE_APP_MOCK_USER_INFO=
# 是否加密
VUE_APP_ENCRYPT=false
# 是否开启 vConsole
VUE_APP_CONSOLE=true
# 是否开启打印 console.log
VUE_APP_HAS_CONSOLE=true
# appid
VUE_APP_APPID=wxcaad75b7fff5659c
# APP 渠道
VUE_APP_CHANNEL=_test
# OSS上传Bucket_Name
VUE_APP_BUCKET_NAME='pica-app-test'
# 本地化存储:0、关闭 1、打开
VUE_APP_CACHE_CONTROL=0
# 域名
VUE_APP_DOMAIN='test1-phome.yunqueyi.com'
NODE_ENV=production
# 自定义 VUE_APP_ENV
VUE_APP_ENV ='uat'
VUE_APP_SERVICE_URL=https://uat-sc.yunqueyi.com
# 阿里云OSS对象存储地址
VUE_APP_OSS_URL=https://pica-h5-uat.yunqueyi.com/
# 模拟用户信息,如果有则发送模拟用户信息,只针对开发模式
VUE_APP_MOCK_USER_INFO=
# 是否加密
VUE_APP_ENCRYPT=false
# 是否开启vConsole
VUE_APP_CONSOLE=
# appid
VUE_APP_APPID=wx342ef0e5afee54a7
# OSS 上传 Bucket_Name
VUE_APP_BUCKET_NAME='pica-app-uat'
# 本地化存储:0、关闭 1、打开
VUE_APP_CACHE_CONTROL=0
# 域名
VUE_APP_DOMAIN='uat-phome.yunqueyi.com'
/h5-submodules-master
/node_modules
module.exports = {
root: true,
parserOptions: {
parser: 'babel-eslint',
sourceType: 'module'
},
env: {
browser: true,
node: true,
es6: true,
},
extends: ['plugin:vue/recommended', 'eslint:recommended'],
// 全局变量
globals: {
'gdp': true,
},
// add your custom rules here
//it is base on https://github.com/vuejs/eslint-config-vue
rules: {
'no-console': 0,
'no-debugger':0,
'comma-spacing': [2, { // 控制逗号前面没有空格,后面必须有空格
'before': false,
'after': true
}],
'arrow-spacing': [2, { // 要求箭头函数前后有空格
'before': true,
'after': true
}],
'no-multi-spaces': 2,// 禁止使用多个空格,
'no-spaced-func': 2,// 禁止 function 标识符和括号之间出现空格,this.getList () 报错
'semi-spacing': [2, {// 强制分号之前不允许有空格
'before': false,
'after': true
}],
'space-infix-ops': 2,// 要求操作符前后必须有空格: 2 + 3 2 === 2 3 > 2
'spaced-comment': [2, 'always'], // 强制在注释中 // 或 /* 使用一致的空格
'array-bracket-spacing': [2, 'never'],// 指定数组的元素之间要以空格隔开(, 后面), never参数:[ 之后和 ] 之前不能带空格,always参数:[ 之后和 ] 之前必须带空格
"space-before-blocks": 2, // if/function等的大括号之前需要有空格
'no-undef-init': 2,// 禁止将变量初始化为 undefined,保存时会将let a = undefined变成let a
'semi': [2, 'always'],//强制结尾必须有分号;
'prefer-const': 2,// 要求使用 const 声明那些声明后不再被修改的变量
'quotes': [2, 'single'],//js中强制使用单引号
'no-multiple-empty-lines': [2, {// 不允许多个空行,最多一行
'max': 1
}],
'vue/order-in-components': ['error', {
'order': [
'el',
'name',
'parent',
'functional',
['delimiters', 'comments'],
['components', 'directives', 'filters'],
'extends',
'mixins',
'inheritAttrs',
'model',
['props', 'propsData'],
'data',
'computed',
'watch',
'LIFECYCLE_HOOKS',
'methods',
['template', 'render'],
'renderError'
]
}],
'vue/attributes-order': ['error', {
'order': [
'DEFINITION',
'LIST_RENDERING',
'CONDITIONALS',
'RENDER_MODIFIERS',
'GLOBAL',
'UNIQUE',
'TWO_WAY_BINDING',
'OTHER_DIRECTIVES',
'OTHER_ATTR',
'EVENTS',
'CONTENT'
]
}],
}
}
.DS_Store
node_modules
/dist
.history/
# local env files
update_*.sh
package-lock.json
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.proxy.js
.version
log/
logs/
# .pica.prompts.js
log
logs
.version
module.exports = {
'autoRouter': false,
'registry': "http://192.168.110.93:4873/",
'type': "h5",
'projectName': "pica-insurance",
'autoConfig': true,
'transition': false,
'useHistoryRouter': true,
'rewriteHtml': false,
'renderToBranch': false,
'srcAlias': "mn-template",
};
FROM nginx
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime &&\
echo "Asia/shanghai" > /etc/timezone
COPY error.html /usr/share/nginx/html/error.html
COPY nginx.conf /etc/nginx/nginx.conf
COPY dist/ /usr/vue/pica-insurance/dist/
RUN chmod -R +rw /usr/share/nginx/html/error.html &&\
chown -R nginx:nginx /usr/share/nginx/html/error.html &&\
chmod -R +rw /usr/vue/pica-insurance/dist/ &&\
chown -R nginx:nginx /usr/vue/pica-insurance/dist/
# pica-insurance
> 请先查看每个目录的 md 文件
## 启动
```shell script
npm run start
```
## 编译打包
```shell script
#开发环境
npm run build:dev
#预发环境
npm run build:pre
#生产环境
npm run build:prod
#测试环境
npm run build:test
```
## 插件配置
```js
module.exports = {
// 插件配置项
pluginOptions: {
// 蛮牛配置
pica: {
// CDN扩展JS资源排序: ['lodash', 'vue', 'vuex']
jsSort: [],
// CDN扩展CSS资源排序: ['vant.css']
cssSort: [],
// 骨架屏渲染的路由(手动配置)
skeletonRoutes: [
{
path: '/demo',
name: 'Demo',
meta: {
skeleton: true
}
},
],
// 启用自动配置骨架屏渲染路由
enableAutoSkeletonRoutes: false,
// 路由模式
routeMode: 'history',
// 骨架屏一次渲染几个路由
dpsLimit: 5,
// dps骨架屏渲染本地服务配置
dpsServer: {}
},
// 图片压缩
tinypng: {
key: ''
},
// 自动路由配置
autoRouting: {
chunkNamePrefix: 'page-'
}
}
};
```
## dpsServer
### port
服务监听端口,默认从8000开始查找可用端口
### proxy
反向代理中间件配置 @See [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware)
如果需要自定义返回:
```js
{
proxy: {
'^/app-gateway': (req, res, next) => {
res.json({ code: 200, data: {} });
res.end();
}
}
}
```
## 图片压缩
> 需要在`vue.config.js`中配置`pluginOptions.tinypng.key`的值。
```shell script
npm run tinypng
```
## 调用生成器
```shell script
vue invoke pica
```
## 根据 pica.prompts 生成封闭式模板
```shell script
npm run core
```
## 使用骨架屏
> 框架默认已开启骨架屏渲染。请查看`vue.config.js`
### 自动根据路由配置
需要在路由页面文件配置`route`
```json
{
"meta": {
"skeleton": true
}
}
```
如需自定义骨架
```json
{
"meta": {
"skeleton": {
"name": "必须和路由定义的name一致",
"path": "匹配当前路由的正则",
"pathname": "访问路由的path地址,例如: /mn/demo",
"skeletonId": "骨架屏路由ID,唯一即可"
}
}
}
```
## Jenkinsfile 构建
在 Jenkinsfile 文件中 `versionType = 'Patch';`,versionType 默认为 Patch,为小版本更新,1.2.3 对应 Major.Minor.Patch。
```bash
npm run build:test versionType:Patch
```
## 后台API接口反向代理
### 本地
如果`VUE_APP_MOCK_USER_INFO`不为空则走代理到本地。并且会在接口地址前加上`/proxy`.
默认会把`/proxy`的接口代理到`http://localhost:8081。
如需修改,请查看项目根目录的`.proxy.js`文件
### 走网关
如果`VUE_APP_MOCK_USER_INFO`为空则走代理到网关。并且会在接口地址前加上`/proxy/app-gateway`.
会把`/proxy`的接口代理到`VUE_APP_SERVICE_URL`环境变量,如该变量为空则默认为``
const path = require('path');
function resolve (dir) {
return path.join(__dirname, dir);
}
module.exports = {
resolve: {
alias: {
'@': resolve('src'),
'mn-template': resolve('node_modules/@pica-cli/vue-cli-plugin-pica-cli-plugin/core/src')
}
}
};
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
],
plugins: [
'@babel/plugin-proposal-object-rest-spread'
]
};
let dpsConfig = require('@pica-core/dps-config');
const externalsDpsConfig = {};
dpsConfig = {
...dpsConfig,
...externalsDpsConfig
};
module.exports = dpsConfig;
module.exports = [
/* {
name: 'payOrder',
url: 'payOrder'
}*/
];
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Error</title>
</head>
<body>
An error occurred on the server when processing the URL. Please contact the system administrator!
</body>
</html>
#!/bin/bash
hasGit=`which git` # 判断是否存在git
msg=${1:-'auto commit'} # 获取终端输入的第一个参数,若为空则为auto commit
if [ ! $hasGit ];then
echo 'Please download git first!';
exit 1;
else
git fetch --all
result=`git symbolic-ref --short -q HEAD` # 获取分支名
current_id=`git log -n 1 origin/release --pretty=format:"%H"`
git reset --soft $current_id
git add .
git commit -m "$msg"
echo "curBranch $result"
git push -f origin $result # 提交代码到github(修改了远程项目名)
fi
worker_processes auto;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#隐藏版本号
server_tokens off;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
gzip on;
client_max_body_size 20m;
server {
listen 80;
server_name_in_redirect off;
server_name *.picahealth.com;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root /usr/vue/pica-insurance/dist/;
# if (-d $request_filename) {
# rewrite ^/(.*)([^/])$ https://$host/pica-insurance/$1$2/ permanent;
# }
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 404 405 500 502 503 504 /error.html;
location = /error.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
}
const path = require('path');
const axios = require('axios');
const fs = require('fs');
const projectName = require('./package.json').name;
const filePath = path.join(__dirname, 'src');
// 先删除文件
function deleOldJson() {
const json_path = path.join(__dirname, 'src/menu_code.json');
try{
fs.unlinkSync(json_path);
}catch(err) {
console.log('没有json 文件');
}
}
deleOldJson();
// 文件夹是不是存在
function isFileExisted(filePath) {
return new Promise((resolve, reject) => {
fs.access(filePath, (err) => {
if (err) {
return reject(false);
} else {
return resolve(true);
}
});
});
}
// 请求接口获取menucode
function getMenuCode() {
const url = 'https://sc.yunqueyi.com/basic-data/menuCode/fetch';
// let url="https://dev-sc.yunqueyi.com/basic-data/menuCode/fetch";
console.log(projectName);
axios.get(url, {params:{'projectName':projectName}})
.then(res => {
const resData = res.data;
if(resData.code == '000000') {
if(!resData.data) {
console.log('接口data 数据为空');
return;
}
console.log(resData.data.menuCodeDtoList);
return writerJsonFile(JSON.stringify(resData.data.menuCodeDtoList));
}else{
console.log('获取接口失败');
}
}).catch(error => {
console.error('请求menu接口失败error', error);
});
}
// 写入json 文件
function writerJsonFile(data) {
const wpath = filePath + '/menu_code.json';
fs.writeFileSync(wpath, data);
console.log('写入文件成功');
}
// 创建目录
function createFiles(filePath) {
fs.mkdir(filePath, function(err) {
if(!err) {
getMenuCode();
}else{
console.log('创建目录失败');
}
});
}
isFileExisted(filePath).then(() => {
return getMenuCode();
}).catch(() => {
// 没有文件创建文件
createFiles(filePath);
});
/* eslint-disable */
const fs = require('fs');
const path = require('path');
const program = require('commander');
const pathError = path.resolve(process.cwd(), './.error-log')
const ResetPathFile = require('@pica-core/reset-pathfile-plugin');
program
.command('mode <mode>')
.description('备份log日志')
.action(function (mode) {
// 判断.error-log文件是否存在
fs.stat(pathError, (err, stats) => {
// 如果不存在,就正常执行
if (err) {
console.log('正常执行!');
} else {
new ResetPathFile(mode, 1);
}
});
});
program
.command('check <value>')
.description('检查.error-log是否已删除')
.action(function (mode) {
// 判断.error-log文件是否存在
fs.stat(pathError, (err, stats) => {
// 如果不存在,就正常执行
if (err) {
console.log('正常执行!');
} else {
// 如果存在,执行下面的脚本
fs.unlink(pathError, (err) => {
if (err) throw err;
console.log('.error-log文件已被删除');
throw new Error('请联系运维同学去oss服务器手动copy版本控制相关文件');
});
}
});
});
program.parse(process.argv);
{
"name": "pica-insurance",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "pica-cli-service serve",
"build": "pica-cli-service build",
"lint": "pica-cli-service lint",
"add-all": "pica-cli-service add-all",
"build-skeleton": "pica-cli-service build-skeleton",
"build:dev": "pica-cli-service build --mode dev",
"build:prod": "pica-cli-service build --mode production",
"build:report": "pica-cli-service build --report",
"build:test": "pica-cli-service build --mode testing",
"build:uat": "pica-cli-service build --mode uat",
"core": "pica-cli-service core",
"img-md5": "pica-cli-service img-md5",
"page": "vue-cli-service page",
"skeleton": "pica-cli-service skeleton",
"tinypng": "pica-cli-service tinypng",
"push": "bash gitPush.sh"
},
"dependencies": {
"axios": "^0.19.2",
"core-js": "^3.6.5",
"dayjs": "^1.8.30",
"dsbridge": "^3.1.4",
"lodash": "^4.17.15",
"md5": "^2.3.0",
"pre-commit": "^1.2.2",
"vant": "^2.8.4",
"vconsole": "^3.3.4",
"vue": "^2.6.11",
"vue-fragment": "^1.5.1",
"vue-router": "^3.3.1",
"vuescroll": "^4.15.1",
"vuex": "^3.1.1"
},
"devDependencies": {
"@babel/plugin-proposal-object-rest-spread": "^7.11.0",
"@pica-cli/pica-cli-framework": "^1.0.11",
"@pica-cli/vue-cli-plugin-pica-cli-plugin": "~1.1.62",
"@pica-core/multiple-versions-plugin": "^1.0.12",
"@pica-core/reset-pathfile-plugin": "^1.0.13",
"@pica-core/web-buried-point": "^1.0.20",
"@pica-kit/page-model": "^1.0.17",
"@vue/cli-plugin-babel": "~4.5.11",
"@vue/cli-plugin-eslint": "~4.5.11",
"@vue/cli-service": "~4.5.11",
"babel-eslint": "^10.1.0",
"babel-plugin-import": "^1.13.0",
"colors-console": "^1.0.3",
"dotenv": "^8.2.0",
"draw-page-structure": "^1.0.8",
"eslint": "^6.7.2",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-vue": "^6.2.2",
"fast-install-puppeteer": "^1.0.5",
"husky": "^3.0.9",
"lint-staged": "^9.4.2",
"node-sass": "^4.13.1",
"postcss-px-to-viewport": "^1.1.1",
"prettier": "^2.3.2",
"prettier-eslint-cli": "^5.0.1",
"puppeteer": "^10.1.0",
"sass": "^1.26.3",
"sass-loader": "^8.0.2",
"vue-skeleton-webpack-plugin": "^1.2.2",
"vue-template-compiler": "^2.6.11",
"webpack-bundle-analyzer": "^4.4.2"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead",
"android 4",
"opera 12"
],
"husky": {
"hooks": {
"pre-commit": "pica-cli-service img-md5 && lint-staged"
}
},
"lint-staged": {
"src/**/*.{js,vue}": [
"eslint --fix",
"git add"
],
"src/**/*.{js,vue,html,css,scss,sass}": [
"prettier-eslint --write",
"git add"
]
},
"pica-cli-service": {}
}
module.exports = {
plugins: {
autoprefixer: {},
'postcss-px-to-viewport': {
viewportWidth: 375, // 视口的宽度,对应的时设计稿的宽度/2,一般为750
viewportHeight: 667, // 视口的高度,对应的是设计稿的高度(也可以不配置)
unitPrecision: 5, // 指定‘px’转换为视口单位值的小数位数(很多时候无法整除)
viewportUnit: 'vw', // 指定需要转换成的视口单位,建议使用vw
selectorBlankList: ['ignore', 'tab-bar'],
minPixelValue: 1,
mediaQuery: false,
exclude:[/Tabbar/]
}
}
};
## 增加骨架屏占位 Dom #skeleton
```html
<div id="skeleton"></div>
<div id="app"></div>
```
## 初始化时间记录
```js
(function (window) {
var dsBridge = window.dsBridge;
function logToLocaleString(method) {
var date = new Date();
console.log(date.toLocaleString() + ':' + date.getMilliseconds(), method);
}
window.onNativeLCEvent = logToLocaleString;
window.onload = function () {
dsBridge && dsBridge.call && dsBridge.call('onLCEvent', 'onload');
logToLocaleString('onload');
}
document.addEventListener('DOMContentLoaded', function () {
dsBridge && dsBridge.call && dsBridge.call('onLCEvent', 'DOMContentLoaded');
logToLocaleString('DOMContentLoaded');
});
document.addEventListener('readystatechange', function () {
if (document.readyState == 'interactive') {
dsBridge && dsBridge.call && dsBridge.call('onLCEvent', 'interactive');
logToLocaleString('interactive');
}
if (document.readyState == 'complete') {
dsBridge && dsBridge.call && dsBridge.call('onLCEvent', 'complete');
dsBridge && dsBridge.call && dsBridge.call('forceHideLoading');
logToLocaleString('complete');
}
})
}(window));
```
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
该目录建议放置所有请求资源代码,可根据业务划分与不同子目录。
import $http from 'mn-template/plugins/http';
// 获取亲友列表
export const listRelatives = () => $http({
method: 'get',
url: '/splitter/live/v2/list',
params: {version: '3.2.1'}
});
// 新增亲友
export const list = params => $http.get('/aaa/bbb', params);
该目录下请放置需要被编译的图片资源,打包运行时,如在`vue.config.js`中配置了:
`pluginOptions.tinypng.key`则会自动压缩
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a
href="https://cli.vuejs.org"
target="_blank"
rel="noopener"
>vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li>
<a
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel"
target="_blank"
rel="noopener"
>babel</a>
</li>
<li>
<a
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint"
target="_blank"
rel="noopener"
>eslint</a>
</li>
</ul>
<h3>Essential Links</h3>
<ul>
<li>
<a
href="https://vuejs.org"
target="_blank"
rel="noopener"
>Core Docs</a>
</li>
<li>
<a
href="https://forum.vuejs.org"
target="_blank"
rel="noopener"
>Forum</a>
</li>
<li>
<a
href="https://chat.vuejs.org"
target="_blank"
rel="noopener"
>Community Chat</a>
</li>
<li>
<a
href="https://twitter.com/vuejs"
target="_blank"
rel="noopener"
>Twitter</a>
</li>
<li>
<a
href="https://news.vuejs.org"
target="_blank"
rel="noopener"
>News</a>
</li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li>
<a
href="https://router.vuejs.org"
target="_blank"
rel="noopener"
>vue-router</a>
</li>
<li>
<a
href="https://vuex.vuejs.org"
target="_blank"
rel="noopener"
>vuex</a>
</li>
<li>
<a
href="https://github.com/vuejs/vue-devtools#vue-devtools"
target="_blank"
rel="noopener"
>vue-devtools</a>
</li>
<li>
<a
href="https://vue-loader.vuejs.org"
target="_blank"
rel="noopener"
>vue-loader</a>
</li>
<li>
<a
href="https://github.com/vuejs/awesome-vue"
target="_blank"
rel="noopener"
>awesome-vue</a>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
import { router, store, vueApp } from '@pica-cli/vue-cli-plugin-pica-cli-plugin/auto';
import '@/router';
console.log('router, store, vueApp: ', router, store, vueApp);
该目录放置Vue第三方插件初始化的文件。该目录会被自动加载。
例如:
```js
import Vue from 'vue';
import { Plugin } from 'vue-fragment';
Vue.use(Plugin);
Vue.property.$test = 'pica';
```
/**
* @name: appHooks
* @author: alan
* @date: 2021-09-13 17:05
* @description:appHooks
* @update: 2021-09-13 17:05
*/
export default {
data() {
return {
};
},
created() {
},
mounted() {
}
};
import $http from 'mn-template/plugins/http';
// 设置axios默认属性
$http.setDefaults({
headers: {}
});
// 设置http配置信息 loading、error、clear、encrypt、mockUserInfo
$http.setOptions({
loading () {
console.log('我重写了默认的loading');
}
});
// 新增前置钩子
$http.addBeforeHook(config => console.log('我是新增的前置钩子', config), 0);
// 新增后置钩子
$http.addAfterHook(response => console.log('我是新增的后置钩子', response), 0);
/**
* @name: router-config
* @author: alan
* @date: 2022-01-04 19:29
* @description:router-config
* @update: 2022-01-04 19:29
*/
export default {
};
/**
* @name: vue-component
* @author: alan
* @date: 2021-11-16 18:03
* @description:vue-component
* @update: 2021-11-16 18:03
*/
import Vue from 'vue';
import PageModel from '@pica-kit/page-model';
Vue.use(PageModel);
/**
* @name: vue-inject
* @author: alan
* @date: 2021-11-16 18:22
* @description:vue-inject
* @update: 2021-11-16 18:22
*/
import Vue from 'vue';
import WebBuriedPoint, {sendBuriedData} from '@pica-core/web-buried-point';
const {name} = require('../../package.json');
Vue.prototype.$sendBuriedData = sendBuriedData;
Vue.use(WebBuriedPoint, {
class_name: name,
url: `${process.env.VUE_APP_SERVICE_URL}/file/log/trace1`,
});
import axios from 'axios';
import { name } from '../package.json';
// 整理路由
const routerObj = {};
const titleObj = {};
let baseUrl = process.env.VUE_APP_BASE_ROUTE_URL || process.env.BASE_URL || '';
let fullHost = location.host || '';
if (fullHost.indexOf('uat-') != -1) {
fullHost = fullHost.replace('uat-', '');
}
if (fullHost.indexOf('http') == -1) {
fullHost = (window.location.protocol || 'https:') + '//' + fullHost;
}
let rMode = 'history';
if (window.location.hash) {
rMode = 'hash';
baseUrl = baseUrl || location.pathname;
}
let projectName = name;
// 处理路由children
function allRouter(ele, spath) {
for (var i = 0; i < ele.length; i++) {
if (ele[i].path) {
let key = '';
if (spath && spath != '/') {
key = spath + '/' + ele[i].path;
} else {
key = ele[i].path;
}
routerObj[key] = key;
titleObj[key] = ele[i].meta && ele[i].meta.title;
if (ele[i].children) {
allRouter(ele[i].children, ele[i].path);
}
}
}
}
// 处理路由
function handleAllRouter(parmsObj) {
parmsObj.mode = parmsObj.mode || rMode;
projectName = parmsObj.projectName || projectName;
baseUrl = parmsObj.baseUrl || baseUrl || '';
const fhost = parmsObj.fullHost || fullHost;
if (baseUrl.substr(baseUrl.length - 1, 1) == '/') {
baseUrl = baseUrl.substr(0, baseUrl.length - 1);
}
if (parmsObj.mode == 'hash') {
baseUrl = baseUrl + '/#';
}
allRouter(parmsObj.routerConfig, '');
const arr = [];
for (var key in routerObj) {
const optionObj = {};
optionObj.url = routerObj[key];
optionObj.fullPath = fhost + baseUrl + routerObj[key];
optionObj.host = fhost;
optionObj.title = titleObj[key];
arr.push(optionObj);
}
sendAllRouterInfo(arr);
}
// 发送请求
function sendAllRouterInfo(data) {
let url = '';
if (process.env.NODE_ENV == 'development') {
url = 'https://dev-sc.yunqueyi.com/basic-data/menuCode/upload';
} else if (process.env.VUE_APP_ENV == 'uat') {
url = 'https://sc.yunqueyi.com/basic-data/menuCode/upload';
}
if (!url) {
return;
}
const obj = {
projectName: projectName,
menuCodeDtoList: data,
};
console.log(obj);
axios({
method: 'post',
url: url,
data: obj,
})
.then((res) => {
console.log('请求接口成功了', res.data);
return;
})
.catch((err) => {
console.log('errerrerr', err);
return;
});
}
export default handleAllRouter;
```js
import { router } from '@pica-cli/vue-cli-plugin-pica-cli-plugin/auto';
import Index from '@/views/home.vue';
const routerConfig = [
{
path: '/',
name: 'Index',
component: Index
},
{
path: '*',
redirect: process.env.VUE_APP_BASE_ROUTE
}
];
router.addRoutes(routerConfig);
```
```js
// vue.config.js
module.exports = {
transpileDependencies: [
'@pica-cli/vue-cli-plugin-pica-cli-plugin'
],
pluginOptions: {
pica: {
jsSort: [],
cssSort: [],
// 配置骨架屏路由
skeletonRoutes: [
{
path: '',
name: 'Index',
meta: {
skeleton: true
}
}
],
enableAutoSkeletonRoutes: false,
routeMode: 'history',
dpsLimit: 5
},
tinypng: {
key: ''
}
}
};
module.exports.publicPath = process.env.VUE_APP_OSS_URL + '/static' + process.env.BASE_URL;
```
该目录放置需要自定义路由过滤器(守卫)的文件。该目录会被自动加载。
默认加载:
* `BudpParamsFilter` 中台参数过滤器
* `ToRootFilter` 清空路由栈并跳转到指定路由为当前首页
例如:
```js
import {AbstractRouterFilter} from '@pica-cli/pica-cli-framework';
export default class Demo extends AbstractRouterFilter {
onBefore (to, from) {
console.log(`from ${from.path} to ${to.path}`);
}
}
```
如需要排除默认加载过滤器:
```js
import {BudpParamsFilter} from '@pica-cli/pica-cli-framework';
// 禁用中台过滤器
BudpParamsFilter.property.matches = () => false;
```
import { router } from '@pica-cli/vue-cli-plugin-pica-cli-plugin/auto';
import Index from '@/views/index.vue';
import {toggleSkeletonRouter} from 'mn-template/plugins/toggleSkeleton.js';
import handleAllRouter from '../public_uat';
const routerConfig = [
{
path: '/',
name: 'Index',
component: Index
},
{
path: '*',
redirect: process.env.VUE_APP_BASE_ROUTE
}
];
router.beforeEach(async (to, from, next) => {
toggleSkeletonRouter(to, from);
next();
});
router.addRoutes(routerConfig);
if (process.env.VUE_APP_ENV == 'uat') {
const routerInfo = {
routerConfig: routerConfig,
};
handleAllRouter(routerInfo);
}
export default {
namespaced: true,
state: {
count: 0
},
mutations: {
add (state, val = 1) {
state.count += val;
}
}
};
@charset "utf-8";
*{
margin: 0;
padding: 0;
}
body{
background-color: $colorBackground;
// color: $color4;
}
a, a:hover, a:active, a:focus{
text-decoration: none;
color: $color4;
}
input{
border: 0;
outline: none;
}
img{
display: block;
border: 0;
width: 100%;
height: 100%;
}
.clear:before,
.clear:after{
content: '';
display: block;
clear: both;
height: 0;
}
input::-webkit-input-placeholder{
color: $color10 !important;
}
input::-moz-placeholder{
color: $color10 !important;
}
input::-ms-input-placeholder{
color: $color10 !important;
}
input[type=search]::-webkit-search-cancel-button{
-webkit-appearance: none;
}
.van-picker__toolbar {
height:45px;
background:rgba(255,255,255,1);
box-shadow:0px -4px 15px 0px rgba(0,0,0,0.05);
}
.van-picker__columns {
background:rgba(255,255,255,1);
box-shadow:0px -4px 15px 0px rgba(0,0,0,0.05);
}
.van-picker__confirm {
color: #15A4AC;
}
.van-picker__title {
font-size:13px;
font-weight:400;
color:rgba(131,139,152,1);
}
.fixed-bottom-20 {
position: fixed;
bottom: 20px;
}
.van-toast__icon {
font-size: 25px;
}
.page-right-content {
position: absolute;
left: -40px;
}
.flex-column {
display: flex;
flex-direction: column;
}
.flex-row {
display: flex;
flex-direction: row;
}
.flex-between {
justify-content: space-between;
}
.flex-around {
justify-content: space-around;
}
.flex-center {
align-items: center;
justify-content: center;
}
.flex-expanded {
flex: 1;
}
.van-action-sheet__item {
&:not(:first-child) {
border-top: 1px solid rgba(216, 216, 216, 0.8);
}
}
.van-cell__left-icon, .van-cell__right-icon {
line-height: 24px !important;
}
#app {
.__vuescroll .__refresh svg.start .active-path, .__vuescroll .__load svg.start .active-path {
stroke: #c8c9cc;
}
.__vuescroll .__refresh svg path, .__vuescroll .__refresh svg rect, .__vuescroll .__load svg path, .__vuescroll .__load svg rect {
fill: #c8c9cc;
}
.__vuescroll .__refresh, .__vuescroll .__load {
display: flex;
justify-content: center;
align-items: center;
font-size: 14px;
color: rgba(0,0,0, 0.5);
}
.__vuescroll .__refresh svg, .__vuescroll .__load svg {
height: 16px;
width: 16px;
}
}
.symbol-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
.echarts-tip {
&-warp {
position: relative;
}
&-background {
background: #FBBD52;
padding: 1px 7px;
border-radius: 2px;
font-size: 13px;
}
&-triangle-down {
width: 0;
height: 0;
position: relative;
left: calc(50% - 4px);
top: 0;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 3px solid #FBBD52;
}
}
html {
color: #000;
background: #fff;
overflow-y: scroll;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%
}
html * {
outline: 0;
-webkit-text-size-adjust: none;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0)
}
html,body {
font-family: PingFangSC-Regular, -apple-system-font, Source Han Sans, Helvetica Neue, sans-serif;
}
body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, code, form, fieldset, legend, input, textarea, p, blockquote, th, td, hr, button, article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {
margin: 0;
padding: 0;
}
/*
input, select, textarea {
font-size: 100%;
}
*/
table {
border-collapse: collapse;
border-spacing: 0;
}
fieldset, img {
border: 0;
}
abbr, acronym {
border: 0;
font-variant: normal;
}
del {
text-decoration: line-through;
}
address, caption, cite, code, dfn, em, th, var {
font-style: normal;
font-weight: 500;
}
ol, ul {
list-style: none;
}
caption, th {
text-align: left;
}
/*
h1, h2, h3, h4, h5, h6 {
font-size: 100%;
font-weight: 500;
}
*/
q:before, q:after {
content: '';
}
sub, sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -.5em;
}
sub {
bottom: -.25em;
}
a:hover {
text-decoration: underline;
}
ins, a {
text-decoration: none;
}
$color1: rgba(15,28,52,1);
$color2: #58FFD2;
$color3: #FFBF38;
$color4: #15A4AC;
$color5: #83FFB5;
$color6: #EB4343;
$color7: #EB7C43;
$color8: linear-gradient(360deg,rgba(21,164,172,1) 0%,rgba(40,210,200,1) 100%);
$color9: #58FFD2;
$color10: #A4AAB3;
$color11: #969DBA;
$color12: #BBBBBB;
$color13: #FFAB00;
$color14: #0000FF;
$color15: linear-gradient(180deg,rgba(255,217,125,1) 0%,rgba(255,167,17,1) 100%);
$color16: #D2D2D2;
$color17: #333333;
$color18: #999999;
$color19: linear-gradient(90deg,rgba(40,210,200,1) 0%,rgba(21,164,172,1) 100%);
$color20: #FF6E00;
$colorA: #4A90E2;
$colorB: #5ACE6D;
$colorBackground: #F5F5F5;
$colorWhite: #fff;
$colorAqi1: #68CB00;
$colorAqi2: #FFC000;
$colorAqi3: #DF2D00;
$colorAqi4: #6F0574;
.top-error-message {
top: 12%;
width: 80%;
}
import Cookies from 'js-cookie';
const VUE_APP_ENV = process.env.VUE_APP_ENV;
const VUE_APP_DOMAIN = process.env.VUE_APP_DOMAIN;
const localDomain = VUE_APP_ENV === 'development' ? window.location.hostname : VUE_APP_DOMAIN;
export const setCookie = (
cname,
cvalue,
params = {
path: '/',
expires: 7,
domain: localDomain,
},
) => {
delCookie(cname);
Cookies.set(cname, cvalue, params);
};
export const getCookie = (
cname,
params = { path: '/', domain: localDomain },
) => {
const data = Cookies.get(cname, params);
return data;
};
export const delCookie = (cname) => {
Cookies.remove(cname, {
path: '/',
domain: localDomain,
});
Cookies.remove(cname, {
path: '/',
domain: '.yunqueyi.com',
});
Cookies.remove(cname, {
path: '/',
});
};
import * as cookieFun from './cookieFun';
export const setCookie = cookieFun.setCookie;
export const getCookie = cookieFun.getCookie;
export const delCookie = cookieFun.delCookie;
export const formatDate = (datetime) => {
var date = new Date(datetime); // 时间戳为10位需*1000,时间戳为13位的话不需乘1000
var year = date.getFullYear(),
month = ('0' + (date.getMonth() + 1)).slice(-2),
sdate = ('0' + date.getDate()).slice(-2),
hour = ('0' + date.getHours()).slice(-2),
minute = ('0' + date.getMinutes()).slice(-2),
second = ('0' + date.getSeconds()).slice(-2);
// 拼接
var result =
year + '-' + month + '-' + sdate + ' ' + hour + ':' + minute + ':' + second;
// 返回
return result;
};
export const formatDay = (datetime) => {
var date = new Date(datetime);
var year = date.getFullYear(),
month = ('0' + (date.getMonth() + 1)).slice(-2),
day = ('0' + date.getDate()).slice(-2);
// 拼接
var result = year + '-' + month + '-' + day;
// 返回
return result;
};
// 判断是否是微信
export function isWeixin() {
const ua = navigator.userAgent.toLowerCase();
return ua.match(/MicroMessenger/i) === 'micromessenger';
}
// 时间戳 格式化为 时分秒(00: 00: 00)
export const formatDuring = (time) => {
const s = Math.floor(time / 1000);
if (!s) {
return '';
}
let t = '';
if (s > -1) {
const hour = Math.floor(s / 3600);
const min = Math.floor(s / 60) % 60;
const sec = s % 60;
if (hour) {
if (hour < 10) {
t = '0' + hour + ':';
} else {
t = hour + ':';
}
}
if (min < 10) {
t += '0';
}
t += min + ':';
if (sec < 10) {
t += '0';
}
t += sec.toFixed(0);
}
return t;
};
export function toDecimal2(x) {
if (isNaN(x)) {
return '';
}
const f = x / 100;
let s = f.toString();
let rs = s.indexOf('.');
if (rs < 0) {
rs = s.length;
s += '.';
}
while (s.length <= rs + 2) {
s += '0';
}
return s;
}
/**
* 通过身份证获取出生日期及性别
* @param idCard 15/18位身份证号码
* @return JSON对象
* sex:0-女、1-男;
* birthDay:yyyy-MM-dd
*/
export function getBirthdayAndSex(idCard) {
const info = {};
const birth = (idCard.length === 18) ? idCard.slice(6, 14) : idCard.slice(6, 12);
// 18位:提取第17位数字;15位:提取最后一位数字
const order = (idCard.length === 18) ? idCard.slice(-2, -1) : idCard.slice(-1);
info.birthDay = (idCard.length === 18) ? ([birth.slice(0, 4),
birth.slice(4, 6), birth.slice(-2)
]).join('-') : ([
'19' + birth.slice(0, 2), birth.slice(2, 4),
birth.slice(-2)
]).join('-');
// 余数为0代表女性,不为0代表男性
info.sex = (order % 2 === 0 ? 0 : 1);
return info;
}
该目录下放置需要路由的页面
> 如开启自动创建路由配置,则会自动把该目录的vue文件自动配置到routers。
#### 以__(两个下划线,例如__foo __.vue)开头和结尾的目录和文件将被忽略。
例如:
```
views/
├── __partial__.vue
├── index.vue
├── users.vue
└── users/
└── _id.vue
```
自动生成路由:
```js
export default [
{
name: 'index',
path: '/',
component: () => import('@/views/index.vue')
},
{
name: 'users',
path: '/users',
component: () => import('@/views/users.vue'),
children: [
{
name: 'users-id',
path: ':id?',
component: () => import('@/views/users/_id.vue')
}
]
}
]
```
如果路由页面采用分文件方式:template 为.vue文件,scss为样式等:
```
views/
└── users/
└── index.vue
```
自动生成路由:
```js
export default [
{
name: 'users',
path: '/users',
component: () => import('@/views/users/index.vue')
}
]
```
如果要使路由参数为必须,则创建一个该参数的目录,然后在该目录中添加`index.vue`。 在上面的示例中,如果将`users/_id.vue`替换为`users/_id/index.vue`,则需要:`id`参数。
### <route> 自定义标签块
如果页面组件有<route>自定义标签块,则该标签内的内容json将与route config合并。
例如,如果index.vue具有以下<route>块:
```vue
<route>
{
"name": "home",
"meta": {
"requiresAuth": true
}
}
</route>
<template>
<h1>Hello</h1>
</template>
```
生成的路由配置如下:
```js
module.exports = [
{
name: 'home',
path: '/',
component: () => import('@/views/index.vue'),
meta: {
requiresAuth: true
}
}
]
```
<template>
<fragment>
<H1 class="flex-row flex-center">
This is Demo Page
</H1>
<div class="flex-row flex-center">
store.count: {{ count }}
</div>
</fragment>
</template>
<script>
export default {
name: 'Home',
computed: {
count () {
return this.$store.state.home.count;
}
},
mounted () {
console.log(this.$route.meta); // {requiresAuth: true}
const a = 1;
let b = 2;
b = 3;
console.log(b);
console.log(a);
},
methods: {
}
};
</script>
<style scoped>
</style>
module.exports = {
transpileDependencies: [
/[/\\]node_modules[/\\][@\\]pica-cli[/\\]vue-cli-plugin-pica-cli-plugin[/\\]core/,
/[/\\]node_modules[/\\][@\\]pica-kit[/\\]page-model/
],
productionSourceMap: false,
pluginOptions: {
pica: {
jsSort: [],
cssSort: [],
skeletonRoutes: [],
enableAutoSkeletonRoutes: false,
routeMode: 'history',
dpsLimit: 5
},
tinypng: {
key: 'kYWBH2Ck5kn4BwJj8QqjcF0mZJBtZwNR'
}
},
chainWebpack: config => {
config.set('externals', {
vue: 'Vue',
vuex: 'Vuex',
'vue-router': 'VueRouter',
'vconsole': 'VConsole',
axios: 'axios',
vant: 'vant',
'crypto-js/crypto-js': 'CryptoJS',
'jsencrypt/bin/jsencrypt': 'JSEncrypt'
});
config.plugins.delete('prefetch');
config.plugins.delete('preload');
}
};
module.exports.publicPath = process.env.VUE_APP_ENV === 'development' && process.env.VUE_APP_IS_LOCAL ? process.env.BASE_URL : `${process.env.VUE_APP_OSS_URL}/static${process.env.BASE_URL}`;
Markdown 格式
0% or
您添加了 0 到此讨论。请谨慎行事。
先完成此消息的编辑!
想要评论请 注册