update 模板

This commit is contained in:
wzz 2021-07-21 12:43:42 +08:00
parent d481ab6057
commit 9d3a875182
52 changed files with 3194 additions and 0 deletions

4
.babelrc Normal file
View File

@ -0,0 +1,4 @@
{
"presets": [["@babel/preset-env", { "modules": false }]],
"plugins": []
}

14
.env Normal file
View File

@ -0,0 +1,14 @@
# 所有环境默认
VUE_APP_VERSION=1.0.0
# 页面 title 前缀
VUE_APP_TITLE=管理平台
# 部署路径
VUE_APP_PUBLIC_PATH=/
# 缓存前缀
VUE_APP_LOCAL_CODE=admin

2
.env.production Normal file
View File

@ -0,0 +1,2 @@

26
.eslintrc.js Normal file
View File

@ -0,0 +1,26 @@
module.exports = {
root: true,
env: {
node: true,
},
extends: [
"plugin:vue/essential",
//关闭ESlint关键代码
// '@vue/standard'
],
parserOptions: {
parser: "babel-eslint",
},
rules: {
//严格的检查缩进问题,不是报错,我们可以关闭这个检查规则,然后在终端输入npm run dev
indent: ["off", 2],
"vue/no-parsing-error": [2, { "x-invalid-end-tag": false }],
//使用eslint时严格模式下报错Missing space before function parentheses的问题意思是在方法名和刮号之间需要有一格空格。
"space-before-function-paren": 0,
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
//关闭prettier
"prettier/prettier": "off",
"vue/no-use-v-if-with-v-for": "off",
},
};

24
.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
package-lock.json

3
babel.config.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = {
presets: ["@vue/cli-plugin-babel/preset"],
};

73
package.json Normal file
View File

@ -0,0 +1,73 @@
{
"name": "antifraud",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"report": "vue-cli-service build --report",
"lint": "vue-cli-service lint"
},
"dependencies": {
"axios": "^0.21.1",
"core-js": "^3.12.1",
"dayjs": "^1.10.4",
"echarts": "^5.1.1",
"element-ui": "^2.15.1",
"lodash": "^4.17.21",
"lowdb": "^1.0.0",
"vant": "^2.12.18",
"vue": "^2.6.12",
"vue-json-excel": "^0.3.0",
"vue-mini-player": "^0.2.1",
"vue-router": "^3.5.1",
"vuex": "^3.6.2",
"vuex-persistedstate": "^4.0.0-beta.3",
"weixin-js-sdk": "^1.6.0",
"xlsx": "^0.16.9"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.5.13",
"@vue/cli-plugin-eslint": "^4.5.13",
"@vue/cli-plugin-router": "^4.5.13",
"@vue/cli-plugin-vuex": "^4.5.13",
"@vue/cli-service": "^4.5.13",
"postcss-px2rem": "^0.3.0",
"amfe-flexible": "^2.2.1",
"babel-eslint": "^10.1.0",
"babel-plugin-component": "^1.1.1",
"babel-plugin-import": "^1.13.3",
"compression-webpack-plugin": "^5.0.2",
"eslint": "^6.8.0",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-vue": "^6.2.2",
"image-webpack-loader": "^7.0.1",
"node-sass": "^5.0.0",
"px2rem-loader": "^0.1.9",
"sass": "^1.32.12",
"sass-loader": "^10.2.0",
"vconsole": "^3.5.1",
"vue-template-compiler": "^2.6.12",
"vue-wxlogin": "^1.0.4",
"webpack-bundle-analyzer": "^4.4.2"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/strongly-recommended",
"eslint:recommended",
"@vue/prettier"
],
"parserOptions": {
"parser": "babel-eslint"
}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}

3
public/config/config.js Normal file
View File

@ -0,0 +1,3 @@
var config = {};
config.API = "";

33
public/index.html Normal file
View File

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<!-- <script charset="utf-8" src="https://map.qq.com/api/gljs?v=1.exp&key=BRPBZ-RLNLJ-XQEFU-FDWTN-VQKI6-M5FVL&libraries=geometry"></script> -->
<script src="<%= VUE_APP_PUBLIC_PATH %>config/config.js"></script>
<!-- <script charset="utf-8" src="https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"></script> -->
<!-- <link rel="icon" href="<%= VUE_APP_PUBLIC_PATH %>/imgs/fanzhalogo.png"> -->
<title>
<%= VUE_APP_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>
<% if (process.env.NODE_ENV==='production' ) { %>
<% for(var css of htmlWebpackPlugin.options.cdn.css) { %>
<link rel="stylesheet" href="<%=css%>" as="style">
<% } %>
<% for(var js of htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%=js%>"></script>
<% } %>
<% } %>
</body>
</html>

46
src/App.vue Normal file
View File

@ -0,0 +1,46 @@
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
import { mapActions, mapState } from "vuex";
export default {
components: {
// myrefresh: () => import("@components/common/refresh")
},
computed: {
...mapState("plugin/loading", {
globalLoading: state => state.globalLoading
}),
...mapState("plugin/page", { doRefresh: "doRefresh" })
},
methods: {
...mapActions("plugin/wechat", ["userInfoFailMsg"])
},
created() {
this.$store.dispatch("plugin/loading/open", "globalLoading");
},
mounted() {}
};
</script>
<style lang="scss">
#app {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
body {
width: 100vw;
height: 100vh;
margin: 0;
background-color: #f5f5f4;
resize: both;
overflow: auto;
}
</style>

17
src/api/index.js Normal file
View File

@ -0,0 +1,17 @@
import { assign, map } from "lodash";
import { service, request } from "./service";
import * as tools from "./tools";
const files = require.context("./modules", true, /\.js$/);
const generators = files.keys().map((key) => files(key).default);
export default assign(
{},
...map(generators, (generator) =>
generator({
service,
request,
tools,
})
)
);

View File

@ -0,0 +1,19 @@
export default ({ request }) => ({
/**
* @params proj code appid
* @return proj code appid
*/
authWx(data = {}) {
return request({
url: `/verification?proj=${data.proj}&code=${data.code}&appid=${data.appid}`,
method: "get",
});
},
loadConfigInfo(data = {}) {
return request({
url: "/usr/jsconfig",
method: "post",
data,
});
},
});

View File

@ -0,0 +1,94 @@
export default ({ request }) => ({
/**
* @description 下载网格长
*/
loadAdminPerson(data) {
return request({
url: `/usr/manager?id=${data.id}`,
method: "get",
});
},
/**
* @description 获取辖区
*/
loadAdminArea() {
return request({
url: `/org/jurisdiction`,
method: "get",
});
},
/**
* @description 随机获取视频url
*/
loadVideoUrl() {
return request({
url: `/video/get`,
method: "get",
});
},
/**
* @description 回答者注册
*/
answererRegister(data = {}) {
return request({
url: `/usr/register`,
method: "post",
data,
});
},
/**
* @description 下载全部题目列表 已打乱
*/
loadQuestionList() {
return request({
url: `/question/get`,
method: "get",
});
},
/**
* @description 下载单题选项
* @params id
*/
loadQuestionItem(data = {}) {
return request({
url: `/question/item/${data.id}`,
method: "get",
});
},
/**
* @description 提交答案
* @params duration location answer
*/
questionSubmit(data = {}) {
return request({
url: `/question/submit`,
method: "post",
data,
});
},
/**
* @description 计数
* @params duration location answer
*/
videoPlayCount(data = {}) {
// let param;
// if (data.id !== "") {
// param = "?id=" + data.id;
// }
return request({
url: `/video/count`,
method: "get",
});
},
/**
* @description 下载大奖信息
* @params id
*/
loadNoticePrice(data = {}) {
return request({
url: `/project/notice`,
method: "get",
});
},
});

View File

@ -0,0 +1,39 @@
export default ({ request }) => ({
/**
* @description 概述信息
*/
loadSummary() {
return request({
url: `/admin/summary`,
method: "get",
});
},
/**
* @description 考核信息
*/
loadAssessment() {
return request({
url: `/admin/assessment`,
method: "get",
});
},
/**
* @description 概述信息
辖区id 秒级时间戳
*/
loadAnalysis(data) {
return request({
url: `/admin/analysis?id=${data.id}&s=${data.start}&e=${data.end}`,
method: "get",
});
},
/**
* @description 奖励信息
*/
loadFee() {
return request({
url: `/admin/fee`,
method: "get",
});
},
});

144
src/api/service.js Normal file
View File

@ -0,0 +1,144 @@
import axios from "axios";
import { get, set } from "lodash";
import { dbGet } from "@/libs/util.db";
import { removeToken } from "@/libs/util.token";
import { errorLog, errorCreate, errorMsgCreate, loadingMsgCreate } from "./tools";
import router from "@/router/index";
import msg from "@/libs/util.message";
import { Toast } from "vant";
import store from "../store";
const serviceConfigApi = config.API;
let toast;
/**
* @description 创建请求实例
*/
function createService() {
// 创建一个 axios 实例
const service = axios.create();
// 请求拦截
service.interceptors.request.use(
(config) => {
const token = dbGet({ path: "token.value" });
set(config, "headers.access-token", token);
set(config, "toast", msg.loading(""));
return config;
},
(error) => {
// 发送失败
console.log(error);
return Promise.reject(error);
}
);
// 响应拦截
service.interceptors.response.use(
(response) => {
// dataAxios 是 axios 返回数据中的 data
const dataAxios = response.data;
// 这个状态码是和后端约定的
const { code } = dataAxios;
//去掉 loading
response.config.toast.clear();
// 根据 code 进行判断
if (code === undefined) {
// 如果没有 code 代表这不是项目后端开发的接口 比如可能是 D2Admin 请求最新版本
return dataAxios;
} else {
// 有 code 代表这是一个后端接口 可以进行进一步的判断
switch (code) {
case 0:
return dataAxios.data;
case 1:
removeToken();
errorMsgCreate(`${dataAxios.msg}`);
location.reload();
return false;
case 2:
errorMsgCreate(`${dataAxios.msg}`);
return false;
default:
// 不是正确的 code
errorCreate(`${dataAxios.msg}: ${response.config.url}`);
break;
}
}
},
(error) => {
// console.log(error);
const status = get(error, "response.status");
let toash = get(error, "response.config.toast");
toash.clear();
switch (status) {
case 400:
error.message = "请求错误";
break;
case 401:
error.message = "未授权,请登录";
break;
case 403:
error.message = "拒绝访问";
break;
case 404:
error.message = `请求地址出错: ${error.response.config.url}`;
break;
case 408:
error.message = "请求超时";
break;
case 500:
error.message = "服务器内部错误";
break;
case 501:
error.message = "服务未实现";
break;
case 502:
error.message = "网关错误";
break;
case 503:
error.message = "服务不可用";
break;
case 504:
error.message = "网关超时";
break;
case 505:
error.message = "HTTP版本不受支持";
break;
default:
break;
}
errorLog(error);
Toast.clear();
return Promise.reject(error);
}
);
return service;
}
/**
* @description 创建请求方法
* @param {Object} service axios 实例
*/
function createRequestFunction(service) {
return function(config) {
const token = dbGet({ path: "token.value" });
const configDefault = {
headers: {
"access-token": token,
"Content-Type": get(config, "headers.Content-Type", "application/json"),
},
timeout: 5000,
baseURL: process.env.NODE_ENV === "production" ? serviceConfigApi : "/apis",
data: {},
};
return service(Object.assign(configDefault, config));
};
}
//
// 用于真实网络请求的实例和请求方法
export const service = createService();
export const request = createRequestFunction(service);

112
src/api/tools.js Normal file
View File

@ -0,0 +1,112 @@
import { Toast } from "vant";
/**
* @description 安全地解析 json 字符串
* @param {String} jsonString 需要解析的 json 字符串
* @param {String} defaultValue 默认值
*/
export function parse(jsonString = "{}", defaultValue = {}) {
let result = defaultValue;
try {
result = JSON.parse(jsonString);
} catch (error) {
//console.log(error);
}
return result;
}
/**
* @description 接口请求返回
* @param {Any} data 返回值
* @param {String} msg 状态信息
* @param {Number} code 状态码
*/
export function response(data = {}, msg = "", code = 0) {
return [200, { code, msg, data }];
}
/**
* @description 接口请求返回 正确返回
* @param {Any} data 返回值
* @param {String} msg 状态信息
*/
export function responseSuccess(data = {}, msg = "成功") {
return response(data, msg);
}
/**
* @description 接口请求返回 错误返回
* @param {Any} data 返回值
* @param {String} msg 状态信息
* @param {Number} code 状态码
*/
export function responseError(data = {}, msg = "请求失败", code = 500) {
return response(data, msg, code);
}
/**
* @description 记录和显示错误
* @param {Error} error 错误对象
*/
let errorLogFlag = false;
export function errorLog(error) {
// 打印到控制台
// if (errorLogFlag) return;
if (process.env.NODE_ENV === "development") {
//console.log(error);
}
// 显示提示
// errorLogFlag = true;
// Message.error({
// content: error.message,
// duration: 5,
// onClose: () => {
// errorLogFlag = false;
// },
// });
}
/**
* @description 创建一个错误
* @param {String} msg 错误信息
*/
export function errorCreate(msg) {
const error = new Error(msg);
errorLog(error);
throw error;
}
/**
* @description 创建一个错误弹窗
* @param {String} msg
*/
let msgFlag = false;
export function errorMsgCreate(msg) {
// 显示提示
if (msgFlag) return;
msgFlag = true;
Toast.fail({
message: msg,
duration: 5000,
onClose: () => {
msgFlag = false;
},
});
}
let loadingFlag = false;
export function loadingMsgCreate(msg) {
// 显示提示
if (loadingFlag) return;
loadingFlag = true;
Toast.loading({
message: msg,
duration: 5000,
forbidClick: true,
onClose: () => {
loadingFlag = false;
},
});
}

91
src/libs/util.db.js Normal file
View File

@ -0,0 +1,91 @@
import low from "lowdb";
import LocalStorage from "lowdb/adapters/LocalStorage";
import { cloneDeep } from "lodash";
//console.log(`${process.env.VUE_APP_LOCAL_CODE}-${process.env.VUE_APP_VERSION}`);
const adapter = new LocalStorage(`${process.env.VUE_APP_LOCAL_CODE}-${process.env.VUE_APP_VERSION}`);
const db = low(adapter);
db.defaults({
sys: {},
database: {},
}).write();
export default db;
/**
* @description 检查路径是否存在 不存在的话初始化
* @param {Object} payload dbName {String} 数据库名称
* @param {Object} payload path {String} 路径
* @param {Object} payload user {Boolean} 区分用户
* @param {Object} payload validator {Function} 数据校验钩子 返回 true 表示验证通过
* @param {Object} payload defaultValue {*} 初始化默认值
* @returns {String} 可以直接使用的路径
*/
export function pathInit({ dbName = "database", path = "", user = true, validator = () => true, defaultValue = "" }) {
const uuid = db.get("database.public.uuid").value() ? db.get("database.public.uuid") : "ghost-uuid";
const currentPath = `${dbName}.${user ? `user.${uuid}` : "public"}${path ? `.${path}` : ""}`;
const value = db.get(currentPath).value();
if (!(value !== undefined && validator(value))) {
db.set(currentPath, defaultValue).write();
}
return currentPath;
}
/**
* @description 将数据存储到指定位置 | 路径不存在会自动初始化
* @description 效果类似于取值 dbName.path = value
* @param {Object} payload dbName {String} 数据库名称
* @param {Object} payload path {String} 存储路径
* @param {Object} payload value {*} 需要存储的值
* @param {Object} payload user {Boolean} 是否区分用户
*/
export function dbSet({ dbName = "database", path = "", value = "", user = false }) {
db.set(
pathInit({
dbName,
path,
user,
}),
value
).write();
}
/**
* @description 获取数据
* @description 效果类似于取值 dbName.path || defaultValue
* @param {Object} payload dbName {String} 数据库名称
* @param {Object} payload path {String} 存储路径
* @param {Object} payload defaultValue {*} 取值失败的默认值
* @param {Object} payload user {Boolean} 是否区分用户
*/
export function dbGet({ dbName = "database", path = "", defaultValue = "", user = false }) {
return cloneDeep(
db
.get(
pathInit({
dbName,
path,
user,
defaultValue,
})
)
.value()
);
}
/**
* @description 获取存储数据库对象
* @param {Object} payload user {Boolean} 是否区分用户
*/
export function database({ dbName = "database", path = "", user = false, validator = () => true, defaultValue = "" } = {}) {
return db.get(
pathInit({
dbName,
path,
user,
validator,
defaultValue,
})
);
}

120
src/libs/util.filter.js Normal file
View File

@ -0,0 +1,120 @@
let dayjs = require("dayjs");
import { round, split, join, size } from "lodash";
import tools from "@/libs/util.tool.js";
const vFilter = {
/**
* @description 没有值返回 -
*/
filterDefaultVal(value) {
if (!value && value !== 0) return "-";
return value;
},
/**
* @description 判断数字 且增加百分号 没有则返回-
*/
filterPercentage(value) {
if (value === "-" || value === 0) return value;
return round(value, 0) + "%";
},
/**
* @description 格式化配速 或分秒结构
*/
filterTimeMS(value) {
//console.log(value);
if (value === "-") return value;
if (value == 0) return "0秒";
if (!value) return 0;
let num = new Number(value);
num = num.toFixed(0);
let mm = parseInt(num / 60);
let ss = num - mm * 60;
return mm + "分" + ss + "秒";
},
/**
* @description =>公里
*/
filterDistanceSum(value) {
if (value === "-" || value === 0) return value;
if (!value) return "-";
let num = new Number(value);
return num / 1000;
},
/**
* @description 格式化时间
* @parmas format => 标准时间格式
*/
filterDayFormat4unix(value, format) {
if (value === "-" || value === 0) return value;
if (!value) return "-";
return dayjs(value).format(format);
},
filterDayFormat(value, format) {
if (value === "-" || value === 0) return value;
if (!value) return "-";
let _val = dayjs(value);
return dayjs(_val).format(format);
},
/**
* @description 没有值返回 -
*/
filterDayErrorDuration(value) {
if (dayjs(value).get("year") > 2010) {
return value;
} else {
return "-";
}
},
/**
* @description 格式化
*/
filterSexVal(value) {
value = Number(value);
switch (value) {
case 1:
return "男";
break;
case 0:
return "女";
break;
default:
return "-";
break;
}
},
/**
* @description 生日计算虚岁
*/
filterSexVal(date) {
if (date === "-" || date === 0) return date;
let _dayjs = dayjs(date);
return dayjs().year() - _dayjs.year();
},
/**
* @description 计算显示最大字数
*/
filterStrSize(value, lens) {
let _split, _res;
let _lens = tools.getByteLen(value);
if (_lens > lens) {
let _curlens = tools.getByteCurLen4Size(value, lens);
//console.log(_curlens);
_split = split(value, "", _curlens);
_split.push("...");
_res = join(_split, "");
} else {
return value;
}
return _res;
},
};
export default vFilter;

11
src/libs/util.js Normal file
View File

@ -0,0 +1,11 @@
import db from "./util.db";
import token from "./util.token";
import url from "./util.url";
const util = {
db,
url,
token,
};
export default util;

32
src/libs/util.message.js Normal file
View File

@ -0,0 +1,32 @@
import { Toast } from "vant";
const msg = {};
msg.success = function(msg) {
Toast.success({
message: msg,
duration: 2000,
showClose: true,
});
};
msg.error = function(msg) {
Toast.fail({
message: msg,
duration: 2000,
showClose: true,
});
};
msg.loading = function(msg) {
let _toast = Toast.loading({
message: msg,
duration: 0,
showClose: true,
// className: "sys-my-toash",
forbidClick: true,
});
return _toast;
};
export default msg;

53
src/libs/util.token.js Normal file
View File

@ -0,0 +1,53 @@
import { dbSet, dbGet } from "./util.db";
import api from "@/api";
import { now } from "lodash";
// 处理用户体系相关判断
const token = {};
/**
* @description 判断本地token是否有效
* @return true => 有效
*/
export function isActiveToken() {
const timeout = dbGet({ path: "token.expired" });
const _token = dbGet({ path: "token.value" });
let _access = true;
if (_token) {
// 注意 毫秒时间戳
if (timeout < now()) {
_access = false;
removeToken();
}
} else {
_access = false;
}
return _access;
}
// 删除token
export function removeToken() {
dbSet({ path: "token", value: {} });
}
export function setToken(token, time) {
const tokenData = { value: token, expired: now() + time * 1000 };
const data = { path: "token", value: tokenData };
dbSet(data);
}
export function getToken() {
return dbGet({ path: "token.value" });
}
export function BSkeySet(SecretKey, time) {
const BSkeyData = { value: SecretKey, expired: time };
const data = { path: "BSkey", value: BSkeyData };
dbSet(data);
}
export function BSkeyGet() {
// 返回 秘钥以及有效期
return dbGet({ path: "BSkey" });
}
export default token;

35
src/libs/util.tool.js Normal file
View File

@ -0,0 +1,35 @@
const tools = {};
export default tools;
export function getByteLen(val) {
let len = 0;
for (let i = 0; i < val.length; i++) {
let a = val.charAt(i);
if (a.match(/[^\x00-\xff]/gi) != null) {
len += 1;
} else {
len += 0.5;
}
}
return len;
}
export function getByteCurLen4Size(val, size) {
let len = 0;
let curlen = 0;
for (let i = 0; i < val.length; i++) {
let a = val.charAt(i);
if (a.match(/[^\x00-\xff]/gi) != null) {
len += 1;
} else {
len += 0.5;
}
curlen += 1;
if (len >= size) {
break;
}
}
return curlen;
}

12
src/libs/util.url.js Normal file
View File

@ -0,0 +1,12 @@
const url = {};
export default url;
url.getQueryString = (name) => {
const locString = window.location.href;
let rs = new RegExp("(^|)" + name + "=([^&]*)(&|$)", "gi").exec(locString),
tmp;
if ((tmp = rs)) {
return decodeURI(tmp[2]);
}
return "";
};

54
src/main.js Normal file
View File

@ -0,0 +1,54 @@
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "@/store/index";
// 自适配 rem
import "amfe-flexible";
import "amfe-flexible/index.js";
// 框架插件
import plugin from "@/plugin/index";
import vueFilter from "@/libs/util.filter.js";
Vue.use(plugin);
// add 全局过滤器
for (let key in vueFilter) {
Vue.filter(key, vueFilter[key]);
}
import Vant from "vant";
import "vant/lib/index.css";
Vue.use(Vant);
import "element-ui/lib/theme-chalk/index.css";
import ElementUI from "element-ui";
Vue.use(ElementUI);
import * as echarts from "echarts";
Vue.prototype.$echarts = echarts;
import { Toast } from "vant";
Vue.prototype.$toast = Toast;
Toast.allowMultiple();
Vue.config.productionTip = false;
// 轻量级 播放器
import vueMiniPlayer from "vue-mini-player";
import "vue-mini-player/lib/vue-mini-player.css";
Vue.use(vueMiniPlayer);
// 导出插件
import JsonExcel from "vue-json-excel";
Vue.component("DownloadExcel", JsonExcel);
// import Vconsole from "vconsole";
// let vConsole = new Vconsole();
// Vue.use(vConsole);
new Vue({
router,
store,
render: (h) => h(App),
}).$mount("#app");

9
src/plugin/index.js Normal file
View File

@ -0,0 +1,9 @@
import msg from "@/libs/util.message";
import api from "@/api";
export default {
install(Vue) {
Vue.prototype.$msg = msg;
Vue.prototype.$api = api;
},
};

84
src/router/index.js Normal file
View File

@ -0,0 +1,84 @@
import Vue from "vue";
import VueRouter from "vue-router";
import { includes, concat, get, values } from "lodash";
import { isActiveToken } from "@/libs/util.token";
import store from "../store";
Vue.use(VueRouter);
import mobile from "./modules/mobile";
import pc from "./modules/pc";
import system from "./modules/system";
const routes = concat(system.children, mobile.children, pc.children);
const router = new VueRouter({
// mode: "history",
base: process.env.VUE_APP_PUBLIC_PATH,
routes,
});
// 路由守卫 进入路由之前
router.beforeEach(async (to, from, next) => {
// 完整cur域名 wxjdk调用
let curHref = window.location.href.split("#")[0];
store.commit("plugin/wechat/setState", { name: "curHref", value: curHref });
store.commit("plugin/wechat/setRedirUrl", { url: to.path });
store.commit("plugin/wechat/setState", { name: "type", value: to.meta.type });
store.dispatch("plugin/loading/open", "globalLoading");
let login = get(to.meta, "login", false);
if (login) {
await store.dispatch("plugin/wechat/userLogin");
next();
}
if (to.meta.auth) {
let _access = isActiveToken();
if (_access) {
next();
} else {
if (to.meta.type === "mobile") {
await store.dispatch("plugin/wechat/callbackWechat");
} else {
store.dispatch("plugin/loading/close", "globalLoading");
next({ path: "/pc/login" });
}
}
} else {
next();
}
});
router.beforeResolve(async (to, from, next) => {
let usrRegister = get(store.getters, "manage/register/userRegister", "");
if (to.meta.type === "mobile" && to.path !== "/mobile/register" && usrRegister) {
let doRegister = get(store.state, "manage.register.doRegister");
if (doRegister === false) {
next({ path: "/mobile/register" });
}
}
let title = get(store.state, "plugin.baseinfo.title");
if (title) {
document.title = title;
} else {
document.title = process.env.VUE_APP_TITLE;
}
// 下拉刷新开关
if (to.meta.refresh) {
store.dispatch("plugin/page/able", "doRefresh");
} else {
store.dispatch("plugin/page/disable", "doRefresh");
}
// 防止屏幕抖动
setTimeout(() => {
store.dispatch("plugin/loading/close", "globalLoading");
}, 500);
next();
});
export default router;

View File

@ -0,0 +1,38 @@
let login = true;
let auth = true;
let refresh = true;
export default {
children: ((pre) => [
// {
// meta: { auth, type: pre, login, refresh },
// path: `/`,
// name: `index`,
// component: () => import("@views/manage/mobile/index"),
// },
// {
// path: `/${pre}/register`,
// name: `${pre}register`,
// component: () => import("@views/manage/mobile/register"),
// meta: { auth, type: pre },
// },
// {
// path: `/${pre}/question`,
// name: `${pre}question`,
// component: () => import("@views/manage/mobile/question"),
// meta: { auth, type: pre },
// },
// {
// path: `/${pre}/result`,
// name: `${pre}result`,
// component: () => import("@views/manage/mobile/result"),
// meta: { auth, type: pre },
// },
// {
// path: `/${pre}/test`,
// name: `${pre}test`,
// component: () => import("@views/manage/indextest"),
// meta: { auth, type: pre, login },
// },
])("mobile"),
};

View File

@ -0,0 +1,12 @@
let login = true;
let auth = true;
export default {
children: ((pre) => [
// {
// path: `/${pre}/login`,
// name: `${pre}login`,
// component: () => import("@views/manage/pc/login"),
// },
])("pc"),
};

View File

@ -0,0 +1,17 @@
const meta = { auth: true };
export default {
children: [
{
// 刷新页面
path: "/",
component: () => import("@views/index"),
},
{
// 刷新页面
path: "/redirect/:path*",
name: "redirect",
component: () => import("@views/redirect"),
},
],
};

29
src/store/index.js Normal file
View File

@ -0,0 +1,29 @@
import Vue from "vue";
import Vuex from "vuex";
import createPersistedState from "vuex-persistedstate";
import plugin from "./modules/plugin";
import manage from "./modules/manage";
import { set } from "lodash";
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
plugin,
manage,
},
plugins: [
createPersistedState({
key: `${process.env.VUE_APP_LOCAL_CODE}-${process.env.VUE_APP_VERSION}-persisted`,
reducer(val) {
let _res = {};
set(_res, "manage", val.manage);
set(_res, "plugin.wechat.appid", val.plugin.wechat.appid);
set(_res, "plugin.baseinfo", val.plugin.baseinfo);
return _res;
},
}),
],
});

View File

@ -0,0 +1,16 @@
/**
* The file enables `@/store/index.js` to import all vuex modules
* in a one-shot manner. There should not be any reason to edit this file.
*/
const files = require.context("./modules", true, /\.js$/);
const modules = {};
files.keys().forEach((key) => {
modules[key.replace(/(\.\/|\.js)/g, "")] = files(key).default;
});
export default {
namespaced: true,
modules,
};

View File

@ -0,0 +1,170 @@
import { set, get, map, isArray, isObject, size, isEmpty } from "lodash";
import apis from "@/api";
import { now } from "lodash";
import router from "@/router/index";
export default {
namespaced: true,
state: {
// bind开关
nowQs: 1,
nowOptions: [],
nowQsList: {},
nowAsw: "",
allQsList: "",
historyQsList: [],
historyOtList: [],
answerQsList: {},
start: "",
duration: "",
submit: false,
errorList: [],
redpackMsg: "恭喜发财",
},
getters: {
qsSize: (state) => {
return size(state.allQsList);
},
errorSize: (state, getters) => {
return size(getters.errorDetailList);
},
errorDetailList: (state) => {
let qsList = state.allQsList;
let opList = state.historyOtList;
let myasw = state.historyQsList;
let curasw = state.errorList;
let _res = [];
for (let index = 0; index < qsList.length; index++) {
const item = qsList[index];
let _qsID = item.ID;
let cur = get(curasw, _qsID, "");
let err = get(myasw, index + 1, "");
// 获取正确答案 没有则回答正确
if (cur !== "" && cur !== err) {
let content = item.Content;
let options = get(opList, index + 1, "");
_res.push({ index: index + 1, cur, err, content, options });
}
}
return _res;
},
},
mutations: {
setState(state, payload) {
set(state, payload.name, payload.value);
},
clearState(state, payload) {
let _val = get(state, payload.name);
if (isArray(_val)) {
set(state, payload.name, []);
} else if (isObject(_val)) {
set(state, payload.name, {});
} else {
set(state, payload.name, "");
}
},
next(state, payload) {
if (payload.qsSize === state.nowQs) return;
set(state, "nowQs", state.nowQs + 1);
},
last(state) {
if (state.nowQs === 1) return;
let lastQs = state.nowQs - 1;
set(state, "nowQs", lastQs);
},
},
actions: {
/**
* @description 保存当前题目答案
*/
save({ state, commit }, payload) {
let qs = state.nowQs;
commit("setState", { name: ["historyQsList", qs], value: state.nowAsw });
commit("setState", { name: ["answerQsList", state.nowQsList.ID], value: state.nowAsw });
commit("next", payload);
},
/**
* @description 下载新题目
*/
async loadQuestion({ commit }, payload) {
const _this = this;
commit("clearState", { name: "allQsList" });
let res = await apis.loadQuestionList();
if (res === false) return;
commit("setState", { name: "allQsList", value: res });
},
/**
* @description 根据nowQs 下载选项
*/
async loadQuestionItem({ state, commit }, payload) {
const _this = this;
commit("clearState", { name: "nowOptions" });
let id = state.nowQsList.ID;
let data = { id };
let res = await apis.loadQuestionItem(data);
if (res === false) return;
commit("setState", { name: "nowOptions", value: res });
commit("setState", { name: ["historyOtList", state.nowQs], value: res });
},
/**
* @description 切换题目
*/
async switchQs({ state, commit, dispatch }, payload) {
let nowQsObj = get(state.allQsList, state.nowQs - 1);
commit("clearState", { name: "nowAsw" });
commit("setState", { name: "nowQsList", value: nowQsObj });
// 判断是否上一题
let nextQsAsw = get(state.historyQsList, state.nowQs, "");
if (nextQsAsw !== "") {
commit("setState", { name: "nowAsw", value: nextQsAsw });
commit("setState", { name: "nowOptions", value: get(state.historyOtList, state.nowQs, "") });
} else {
await dispatch("loadQuestionItem");
}
},
/**
* @description 提交题目
*/
async questionSubmit({ state, commit, dispatch, rootState }, payload) {
const _this = this;
let id = state.nowQsList.ID;
let duration = parseInt((now() - state.start) / 1000);
let location = rootState.plugin.wechat.location;
let answer = state.answerQsList;
let inarea = rootState.plugin.wechat.inArea === true ? "是" : "否";
// console.log(location, inarea);
let res = await apis.questionSubmit({ duration, location, answer, inarea });
if (res === false) return false;
commit("setState", { name: "errorList", value: res.Reply });
commit("setState", { name: "duration", value: duration });
commit("setState", { name: "redpackMsg", value: res.Message });
// commit("setState", { name: "submit", value: true });
},
/**
* @description 首次进入
*/
async firstQs({ state, commit, dispatch }, payload) {
commit("setState", { name: "nowQs", value: 1 });
commit("setState", { name: "submit", value: false });
commit("setState", { name: "start", value: now() });
},
/**
* @description 首次进入清空保存项
*/
async clearQuestion({ commit }, payload) {
commit("clearState", { name: "historyQsList" });
commit("clearState", { name: "historyOtList" });
commit("clearState", { name: "answerQsList" });
},
},
};

View File

@ -0,0 +1,78 @@
import { set, get, map, isArray, isObject, size, isEmpty } from "lodash";
import apis from "@/api";
import router from "@/router/index";
import { Toast } from "vant";
import msg from "@/libs/util.message";
export default {
namespaced: true,
state: {
user: {},
doRegister: false,
adminAreaList: "",
adminList: "",
},
getters: {
userRegister: (state) => {
let Mobile = get(state.user, "Mobile", "");
let IDCard = get(state.user, "IDCard", "");
let AdminID = get(state.user, "AdminID", "");
if (isEmpty(Mobile) || isEmpty(IDCard) || isEmpty(AdminID)) {
return true;
} else {
return false;
}
},
},
mutations: {
setState(state, payload) {
set(state, payload.name, payload.value);
},
clearState(state, payload) {
let _val = get(state, payload.name);
if (isArray(_val)) {
set(state, payload.name, []);
} else if (isObject(_val)) {
set(state, payload.name, {});
} else {
set(state, payload.name, "");
}
},
},
actions: {
/**
* 获取网格长数据
*/
async loadAdminPerson({ commit }, data) {
commit("clearState", { name: "adminList" });
if (isEmpty(data)) {
return;
}
let res = await apis.loadAdminPerson(data);
if (res === false) return;
commit("setState", { name: "adminList", value: res });
},
/**
* 获取辖区数据
*/
async loadAdminArea({ commit, state }) {
commit("clearState", { name: "adminAreaList" });
let res = await apis.loadAdminArea();
if (res === false) return;
commit("setState", { name: "adminAreaList", value: res });
},
/**
* 提交注册
*/
async answererRegister({ commit, state }, data) {
let res = await apis.answererRegister(data);
if (res === false) return;
commit("plugin/tools/setState", { name: "searchList", value: res }, { root: true });
commit("setState", { name: "doRegister", value: true });
return true;
},
},
};

View File

@ -0,0 +1,107 @@
import { set, get, map, isArray, isObject, size, cloneDeep } from "lodash";
import apis from "@/api";
import router from "@/router/index";
import { Toast } from "vant";
import msg from "@/libs/util.message";
let dayjs = require("dayjs");
export default {
namespaced: true,
state: {
baseinfo: [],
adminInfo: [],
answerInfo: [],
feeInfo: [],
start: "",
end: "",
id: 0,
answerSearchNumber: "",
},
getters: {
adminInfo: (state) => {
let adminInfo = cloneDeep(state.adminInfo);
return map(adminInfo, (item) => {
item.rate = ((item.ParticipateCount / item.PeopleCount) * 100).toFixed(1) + "%";
item.curRate = ((item.ParticipateCount / item.PeopleCount) * 100).toFixed(1);
return item;
});
},
},
mutations: {
setState(state, payload) {
let name = payload.name;
let value = payload.value;
if (!isArray(name)) {
set(state, name, value);
} else {
if (size(name) === size(value) && isArray(value)) {
map(name, (item, index) => set(state, item, get(value, index)));
} else {
map(name, (item) => set(state, item, value));
}
}
},
clearState(state, payload) {
let _val = get(state, payload.name);
if (isArray(_val)) {
set(state, payload.name, []);
} else if (isObject(_val)) {
set(state, payload.name, {});
} else {
set(state, payload.name, "");
}
},
},
actions: {
/**
* 获取 基础信息
*/
async loadSummary({ commit, state }) {
const _this = this;
commit("clearState", { name: "baseinfo" });
let res = await apis.loadSummary();
if (res === false) return;
commit("setState", { name: "baseinfo", value: res });
},
/**
* 获取 考核信息
*/
async loadAssessment({ commit, state }) {
const _this = this;
commit("clearState", { name: "adminInfo" });
let res = await apis.loadAssessment();
if (res === false) return;
commit("setState", { name: "adminInfo", value: res });
},
/**
* 获取 用户信息
*/
async loadAnalysis({ commit, state }) {
const _this = this;
commit("clearState", { name: "answerInfo" });
let id = state.id;
if (id === 0) id = "";
let data = { id, start: state.start, end: state.end };
let res = await apis.loadAnalysis(data);
if (res === false) return;
//console.log(res);
commit("setState", { name: "answerInfo", value: res });
},
/**
* 获取 奖励发放时间
*/
async loadFee({ commit, state }) {
commit("clearState", { name: "feeInfo" });
let res = await apis.loadFee();
if (res === false) return;
//console.log(res);
commit("setState", { name: "feeInfo", value: res });
},
},
};

View File

@ -0,0 +1,16 @@
/**
* The file enables `@/store/index.js` to import all vuex modules
* in a one-shot manner. There should not be any reason to edit this file.
*/
const files = require.context("./modules", true, /\.js$/);
const modules = {};
files.keys().forEach((key) => {
modules[key.replace(/(\.\/|\.js)/g, "")] = files(key).default;
});
export default {
namespaced: true,
modules,
};

View File

@ -0,0 +1,59 @@
import { set, get, map, isArray, isObject, size, isEmpty } from "lodash";
import apis from "@/api";
import router from "@/router/index";
import { Toast } from "vant";
import msg from "@/libs/util.message";
export default {
namespaced: true,
state: {
title: "",
orgName: "",
},
getters: {},
mutations: {
setState(state, payload) {
let name = payload.name;
let value = payload.value;
if (!isArray(name)) {
set(state, name, value);
} else {
if (size(name) === size(value) && isArray(value)) {
map(name, (item, index) => set(state, item, get(value, index)));
} else {
map(name, (item) => set(state, item, value));
}
}
},
clearState(state, payload) {
let _val = get(state, payload.name);
if (isArray(_val)) {
set(state, payload.name, []);
} else if (isObject(_val)) {
set(state, payload.name, {});
} else {
set(state, payload.name, "");
}
},
},
actions: {
/**
* 设置页面title
*/
setWebBaseInfo({ state, commit, rootState }) {
let user = rootState.manage.register.user;
let orgName = user.OrgName;
let ProJName = user.ProjectName;
commit("setState", { name: ["orgName", "title"], value: [orgName, ProJName] });
},
/**
* 是否首页
*/
async getTitleSet({ state, commit, rootState }) {
let title = state.title;
//console.log(title);
},
},
};

View File

@ -0,0 +1,27 @@
import { set } from "lodash";
export default {
namespaced: true,
state: {
// bind开关
addDeviceId: false,
},
mutations: {
setVisible(state, payload) {
set(state, payload.dialogName, payload.dialogVisible);
},
},
actions: {
/**
* @description 开启弹窗
*/
open({ commit }, name) {
commit("setVisible", { dialogName: name, dialogVisible: true });
},
/**
* @description 关闭弹窗
*/
close({ commit }, name) {
commit("setVisible", { dialogName: name, dialogVisible: false });
},
},
};

View File

@ -0,0 +1,39 @@
import { set } from "lodash";
export default {
namespaced: true,
state: {
// 全局开关
globalLoading: true,
refreshLoading: false,
},
mutations: {
setVisible(state, payload) {
set(state, payload.name, payload.visible);
},
},
actions: {
/**
* @description 开启对应加载条
*/
open({ commit }, name) {
commit("setVisible", { name: name, visible: true });
},
/**
* @description 关闭对应加载条
*/
close({ commit }, name) {
commit("setVisible", { name: name, visible: false });
},
/**
* @description 页面刷新
*/
refresh({ commit, dispatch }) {
// commit("setVisible", { name: "refreshLoading", visible: false });
setTimeout(() => {
dispatch("close", "refreshLoading");
}, 1000);
},
},
};

View File

@ -0,0 +1,28 @@
import { set } from "lodash";
export default {
namespaced: true,
state: {
doRefresh: true,
},
mutations: {
setVisible(state, payload) {
set(state, payload.name, payload.visible);
},
},
actions: {
/**
* @description 展示
*/
able({ commit }, name) {
commit("setVisible", { name: name, visible: true });
},
/**
* @description 隐藏
*/
disable({ commit }, name) {
commit("setVisible", { name: name, visible: false });
},
},
};

View File

@ -0,0 +1,29 @@
import { set } from "lodash";
export default {
namespaced: true,
state: {
showPos: false,
showHls: false,
},
mutations: {
setVisible(state, payload) {
set(state, payload.comName, payload.visible);
},
},
actions: {
/**
* @description 展示
*/
display({ commit }, name) {
commit("setVisible", { comName: name, visible: true });
},
/**
* @description 隐藏
*/
hide({ commit }, name) {
commit("setVisible", { comName: name, visible: false });
},
},
};

View File

@ -0,0 +1,14 @@
import { set } from "lodash";
export default {
namespaced: true,
state: {
duration: "", // 日,周,月,年的英文 与dayjs (add, subtract)方法强关联 如果参数值变更 请修改相关方法
},
mutations: {
setState(state, payload) {
set(state, payload.name, payload.value);
},
},
actions: {},
};

View File

@ -0,0 +1,58 @@
import { set, get, map, isArray, isObject, size, isEmpty } from "lodash";
import apis from "@/api";
export default {
namespaced: true,
state: {
searchList: [],
checkVal: "",
videoUrl: "",
noticeList: [],
},
mutations: {
setState(state, payload) {
set(state, payload.name, payload.value);
},
clearState(state, payload) {
let _val = get(state, payload.name);
if (isArray(_val)) {
set(state, payload.name, []);
} else if (isObject(_val)) {
set(state, payload.name, {});
} else {
set(state, payload.name, "");
}
},
},
actions: {
/**
* 随机获取一个视频地址
*/
async loadVideoUrl({ commit, state, dispatch }) {
const _this = this;
let res = await apis.loadVideoUrl();
if (res === false) return;
commit("setState", { name: "videoUrl", value: res });
},
/**
* 视频播放计数
*/
async videoPlayCount() {
let res = await apis.videoPlayCount();
if (res === false) return;
},
/**
* 头部滚动条 数据
*/
async loadNoticePrice({ commit, state }) {
const _this = this;
let res = await apis.loadNoticePrice();
commit("clearState", { name: "noticeList" });
if (res === false) return;
commit("setState", { name: "noticeList", value: res });
},
},
};

View File

@ -0,0 +1,313 @@
import util from "@/libs/util";
import { isActiveToken, setToken, removeToken } from "@/libs/util.token";
import { set, includes, isEmpty, split } from "lodash";
import apis from "@/api";
import router from "@/router/index";
import { Toast } from "vant";
import wx from "weixin-js-sdk";
export default {
namespaced: true,
state: {
code: "",
appid: "",
token: "",
loginCount: 0,
wx: "",
// firstIn: true,
curHref: "",
redirect_uri: "",
orgin: "https://www.yyjishu.com/antifraud/#",
location: "",
inArea: false,
proj: "",
isPcLoginSuccess: false,
type: "",
},
getters: {
isSuperAdmin: (state, getters, rootState) => {
let user = rootState.manage.register.user;
if (!user) return false;
if (user.Role === "街道办") {
return true;
} else {
return false;
}
},
},
mutations: {
setState(state, payload) {
set(state, payload.name, payload.value);
},
countLogin(state) {
set(state, "loginCount", state.loginCount + 1);
},
setRedirUrl(state, payload) {
let host = location.origin + process.env.VUE_APP_PUBLIC_PATH + "#";
set(state, "redirect_uri", host + payload.url);
},
},
actions: {
/**
* @description 无token登录流程
*/
async userLogin({ state, commit, dispatch }) {
let _doAuth = await dispatch("getUrlCode");
if (_doAuth) {
let _authSuccess = await dispatch("authUser");
if (!_authSuccess) {
Toast("用户鉴权失败,建议重新打开页面!");
}
await dispatch("replaceUrl");
}
},
/**
* @description 替换当前页面
*/
async replaceUrl({ state, commit, dispatch }) {
let redirUrl = state.redirect_uri;
window.location.href = redirUrl;
},
/**
* @description 回调无感登录
*/
async callbackWechat({ commit, dispatch }) {
let url = "https://open.weixin.qq.com/connect/oauth2/authorize";
let appid = "wxd69449ebd878fa6d";
let redirect_uri = "https%3A%2F%2Fwww.yyjishu.com%2Fantifraud%2F%23%2F%3Fproj%3D1%26appid%3Dwxd69449ebd878fa6d";
let response_type = "code";
let scope = "snsapi_userinfo#wechat_redirect";
location.href = `${url}?appid=${appid}&redirect_uri=${redirect_uri}&response_type=${response_type}&scope=${scope}`;
},
/**
* @description 获取url里 code appid proj值 并储存
*/
async getUrlCode({ commit, dispatch }) {
let code = util.url.getQueryString("code");
let appid = util.url.getQueryString("appid");
let proj = util.url.getQueryString("proj");
if (isEmpty(code) || isEmpty(appid) || isEmpty(proj)) {
return false;
}
commit("setState", { name: "code", value: code });
commit("setState", { name: "appid", value: appid });
commit("setState", { name: "proj", value: proj });
return true;
// await dispatch("userLogin");
},
/**
* @description 用户鉴权
*/
async authUser({ state, commit, dispatch }) {
let res = await apis.authWx({ code: state.code, appid: state.appid, proj: state.proj });
if (res === false) {
return false;
}
let { token, expirein, user } = res;
commit("setState", { name: "token", value: token });
commit("manage/register/setState", { name: "user", value: user }, { root: true });
setToken(token, expirein);
// 设置页面基础信息
await dispatch("plugin/baseinfo/setWebBaseInfo", {}, { root: true });
return true;
},
/**
* @description 用户token失效 提示信息
*/
userInfoFailMsg() {
Toast.fail({
message: "用户登录失败\n请关闭后重试",
overlay: true,
duration: 0,
});
},
layout({ commit }) {
removeToken();
// commit("setState", { name: "firstIn", value: true });
commit("setState", "token");
},
/**
* @description 微信jdk 鉴权
*/
async wxConfig({ state, commit, dispatch }) {
//通过config接口注入权限验证配置 只要调后端接口就可以了。后端会把那些信息处理好,然后通过一个接口返给你这些参数
let appId = state.appid;
let uri = state.curHref;
//console.log(appId, uri);
let res = await apis.loadConfigInfo({ appid: appId, uri });
if (res === false) {
//console.log("wxconfig 注入失败");
return;
}
let timestamp = res.Timestamp;
let signature = res.Signature;
let nonceStr = res.NonceStr;
wx.config({
debug: false,
appId,
timestamp,
signature,
nonceStr,
jsApiList: [
"checkJsApi",
"onMenuShareTimeline",
"onMenuShareAppMessage",
"onMenuShareQQ",
"onMenuShareWeibo",
"onMenuShareQZone",
"hideMenuItems",
"showMenuItems",
"hideAllNonBaseMenuItem",
"showAllNonBaseMenuItem",
"translateVoice",
"startRecord",
"stopRecord",
"onVoiceRecordEnd",
"playVoice",
"onVoicePlayEnd",
"pauseVoice",
"stopVoice",
"uploadVoice",
"downloadVoice",
"chooseImage",
"previewImage",
"uploadImage",
"downloadImage",
"getNetworkType",
"openLocation",
"getLocation",
"hideOptionMenu",
"showOptionMenu",
"closeWindow",
"scanQRCode",
"chooseWXPay",
"openProductSpecificView",
"addCard",
"chooseCard",
"openCard",
],
});
wx.error(function(res) {
Toast(res);
});
commit("setState", { name: "wx", value: wx });
},
/**
* 获取地理位置
*/
getLocation({ commit, state, dispatch }) {
return new Promise((resolve) => {
state.wx.ready(function() {
// config信息验证后会执行ready方法所有接口调用都必须在config接口获得结果之后config是一个客户端的异步操作所以如果需要在页面加载时就调用相关接口则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口则可以直接调用不需要放在ready函数中。
state.wx.getLocation({
type: "gcj02",
success: function(res) {
resolve(res);
},
fail: function(res) {
Toast(res);
resolve(res);
},
});
});
});
},
async isInArea({ commit, state, dispatch }) {
await dispatch("wxConfig");
let _getSuceess = dispatch("getLocation");
_getSuceess.then((res) => {
let { latitude, longitude } = res;
console.log(latitude, longitude);
if (!latitude || !longitude) return;
commit("setState", { name: "location", value: latitude + "," + longitude });
dispatch("isPointInPolygon");
});
},
isPointInPolygon({ state, commit }) {
let path = [
new TMap.LatLng(29.943883, 121.602682),
new TMap.LatLng(29.955313, 121.602796),
new TMap.LatLng(29.957481, 121.606207),
new TMap.LatLng(29.960929, 121.605297),
new TMap.LatLng(29.961914, 121.602568),
new TMap.LatLng(29.97876, 121.603023),
new TMap.LatLng(29.978681, 121.6091),
new TMap.LatLng(29.980653, 121.609911),
new TMap.LatLng(29.980753, 121.60585),
new TMap.LatLng(29.988618, 121.60577),
new TMap.LatLng(29.988871, 121.606063),
new TMap.LatLng(29.994632, 121.605825),
new TMap.LatLng(29.994702, 121.606464),
new TMap.LatLng(29.996293, 121.606357),
new TMap.LatLng(29.996546, 121.607156),
new TMap.LatLng(29.998391, 121.606758),
new TMap.LatLng(29.998908, 121.605797),
new TMap.LatLng(29.999143, 121.605863),
new TMap.LatLng(30.001682, 121.608335),
new TMap.LatLng(30.001682, 121.608335),
new TMap.LatLng(30.008685, 121.615431),
new TMap.LatLng(30.009024, 121.618805),
new TMap.LatLng(30.003759, 121.620452),
new TMap.LatLng(30.002604, 121.617118),
new TMap.LatLng(29.993428, 121.620547),
new TMap.LatLng(29.993025, 121.619473),
new TMap.LatLng(29.992184, 121.619874),
new TMap.LatLng(29.991971, 121.620004),
new TMap.LatLng(29.992139, 121.620845),
new TMap.LatLng(29.987921, 121.623377),
new TMap.LatLng(29.989076, 121.625925),
new TMap.LatLng(29.984816, 121.628779),
new TMap.LatLng(29.984911, 121.632577),
new TMap.LatLng(29.979536, 121.635368),
new TMap.LatLng(29.980577, 121.637394),
new TMap.LatLng(29.975834, 121.643542),
new TMap.LatLng(29.97771, 121.641016),
new TMap.LatLng(29.979254, 121.643073),
new TMap.LatLng(29.97518, 121.647262),
new TMap.LatLng(29.9688, 121.636516),
new TMap.LatLng(29.965233, 121.642626),
new TMap.LatLng(29.964734, 121.637667),
new TMap.LatLng(29.961819, 121.640147),
new TMap.LatLng(29.960027, 121.636711),
new TMap.LatLng(29.963852, 121.632747),
new TMap.LatLng(29.960842, 121.630316),
new TMap.LatLng(29.958927, 121.631453),
new TMap.LatLng(29.956739, 121.626875),
new TMap.LatLng(29.956126, 121.627974),
new TMap.LatLng(29.953059, 121.626664),
new TMap.LatLng(29.950817, 121.624055),
new TMap.LatLng(29.951889, 121.622536),
new TMap.LatLng(29.951689, 121.614817),
new TMap.LatLng(29.949841, 121.608112),
new TMap.LatLng(29.948257, 121.605613),
new TMap.LatLng(29.943133, 121.603358),
];
let _resLos = split(state.location, ",");
let [lat, lng] = _resLos;
// console.log(lat, lng);
let userPot = new TMap.LatLng(lat, lng);
let res = TMap.geometry.isPointInPolygon(userPot, path);
// console.log(res);
// if (res === false) {
// let test = [
// new TMap.LatLng(29.816871, 121.486682),
// new TMap.LatLng(29.818024, 121.48849),
// new TMap.LatLng(29.816262, 121.490207),
// new TMap.LatLng(29.814805, 121.488564),
// ];
// res = TMap.geometry.isPointInPolygon(userPot, test);
// }
commit("setState", { name: "inArea", value: res });
},
},
};

43
src/styles/_var.scss Normal file
View File

@ -0,0 +1,43 @@
/**
* 全局变量
* 命名规范 遵守语义化命名lg-大号md-中号base-基本号sm-小号mini-超小号
*/
$font-weight-base: bold;
$font-weight-none: 200px;
$border-color-dark: #e6e6e6;
$border-color-base: #dcdfe6;
$border-color-light: #e4e7ed;
$border-color-lighter: #ebeef5;
$border-color-xlight: #f2f6fc;
$font-color-primary: #303133;
$font-color-regular: #606266;
$font-color-secondary: #909399;
$font-size-lg: xx-large;
$font-size-md: large;
$font-size-base: middle;
$font-size-sm: small;
$font-size-mini: x-small;
$color-backgroud-primay: white;
$color-primary: #409eff;
$color-important: #222222;
$color-common: #606060;
$color-desc: #909090;
$color-disabled: #bfbfbf;
$color-success: #67c23a;
$color-error: #f56c6c;
$color-warn: #e6a23c;
$color-info: #909399;
$color-lighting: #9013fe;
$line-height-none: 1;
$line-height-compact: 1.3;
$line-height-base: 1.5;
$line-height-loose: 1.7;
$line-pc-margin: 20px;

490
src/styles/all.scss Normal file
View File

@ -0,0 +1,490 @@
@import './settings.scss';
$mobile-color-main: #ee6a59;
$mobile-color-font: #3e3c36;
$mobile-color-font-secend: #878787;
$mobile-color-font-contrast: white;
$mobile-color-title: grey;
$mobile-font-thin: 300px;
$mobile-font-bold: bold;
$mobile-font-bolder: bolder;
$mobile-padding-left: 1rem;
.icon-large {
width: 8rem;
height: 8rem;
fill: currentColor;
overflow: hidden;
}
.icon-big {
width: 5rem;
height: 5rem;
fill: currentColor;
overflow: hidden;
}
.icon-small {
width: 1.2rem;
height: 1.2rem;
fill: currentColor;
overflow: hidden;
}
.icon-button {
width: 3rem;
height: 2rem;
fill: currentColor;
overflow: hidden;
}
.icon-button-middle {
width: 4rem;
height: 3rem;
fill: currentColor;
overflow: hidden;
}
.icon-button-big {
width: 5rem;
height: 2rem;
fill: currentColor;
overflow: hidden;
}
.mobile-box-index {
@include wh(100%, auto);
padding: 0.7rem $mobile-padding-left;
background-color: white;
.box-title {
@include sc($mobile-font-base, $mobile-color-font);
text-align: start;
line-height: 1.2rem;
padding-bottom: 0.5rem;
}
.box-secend-title {
@include sc($mobile-font-sm, $mobile-color-font-secend);
text-align: end;
line-height: 1.2rem;
}
.box-map {
@include border-radius;
}
.content {
@include sc($mobile-font-base, $mobile-color-font);
}
.content-sm {
@include sc($mobile-font-sm, $mobile-color-font);
}
.grey-backgd {
background-color: #f7f8f9;
}
.icon {
width: $mobile-font-base;
height: $mobile-font-base;
}
}
.health-box-index {
@include border-radius;
@include wh(100%, auto);
background-color: rgb(250, 130, 225, 0.15);
margin-bottom: 1rem;
padding: 0.5rem 0.2rem;
.col-name {
@include sc($mobile-font-base, #3e3c36);
font-weight: bold;
}
.col-unit {
@include sc($mobile-font-sm, #3e3c36);
}
.col-content {
@include sc($mobile-font-bg, #3e3c36);
font-weight: bold;
line-height: 3.5rem;
text-align: start;
padding-left: 1rem;
}
.col-icon {
width: $mobile-font-bg;
height: $mobile-font-bg;
}
}
.mobile-box-user {
padding-top: 0.5rem;
.user-name {
@include sc($mobile-font-md, $mobile-color-font);
font-weight: normal;
line-height: 2.5rem;
text-align: start;
}
.org-name {
@include sc($mobile-font-xsm, $mobile-color-font);
font-weight: bold;
line-height: 2.5rem;
margin-left: 0.1rem;
}
.user-avatar {
@extend .icon-big;
vertical-align: -0.15rem;
}
.user-avatar-switch {
width: 2rem;
height: 2.8rem;
fill: currentColor;
overflow: hidden;
position: absolute;
top: 4.4rem;
left: 4rem;
}
.user-small {
@extend .icon-small;
vertical-align: -0.24rem;
}
}
.mobile-device-button {
background-color: rgb(55, 115, 180, 0.15);
border-radius: 1.5rem;
padding: 0.1rem 0.5rem;
.col-content {
@include sc($mobile-font-sm, #5d9eff);
}
.col-icon {
@extend .icon-small;
vertical-align: -0.3rem;
}
}
.mobile-postion-box {
padding: 0.5rem 0.2rem;
background-color: rgb(0, 0, 0, 0);
.icon {
@extend .icon-small;
vertical-align: -0.2rem;
}
.position {
@include sc($mobile-font-base, #5d9eff);
}
.time {
@include sc($mobile-font-sm, #878787);
}
}
.mobile-rank-box {
padding: 1.2rem 1rem;
@include sc($mobile-font-base, #3e3c36);
.col-left {
text-align: start;
}
.col-right {
text-align: end;
}
}
.mobile-share-index {
@include wh(auto, auto);
min-height: 100vh;
max-width: 100vw;
background-color: #f6f7f8;
padding: 3.5rem 1rem 2rem 1rem;
.mobile-share-box {
@include border-radius;
background-color: #ffffff;
padding: 1.2rem 1.2rem 1.2rem 1.2rem;
margin-bottom: 1rem;
.code-msg {
@include sc($mobile-font-sm, #303030);
}
.main-msg {
@include sc($mobile-font-base, #303030);
font-weight: bold;
}
}
}
.mobile-device-box {
@include wh(auto, auto);
@include border-radius;
max-width: 100vw;
padding: 0.5rem;
// margin: 0 2rem;
.device-icon {
margin-top: 0.5rem;
// @extend .icon-button-middle;
}
.main-info {
@include sc($mobile-font-base, #000000);
line-height: 2rem;
}
.secend-info {
@include sc($mobile-font-sm, #3e3c36);
text-align: start;
}
.unbind-icon {
@extend .icon-button-middle;
}
}
.mobile-dvtype-index {
background-color: #f6f7f8;
@include wh(auto, auto);
min-height: 100vh;
max-width: 100vw;
padding: 1rem 1.5rem;
.mobile-dvtype-box {
@extend .mobile-device-box;
background-color: #ffffff;
margin-bottom: 2rem;
}
.button-icon {
@extend .icon-button-big;
vertical-align: -1.5rem;
}
}
.mobile-device-advice {
@include wh(auto, auto);
@include full-screen;
background-color: #0b0e15;
.main-icon {
width: auto;
height: auto;
fill: currentColor;
overflow: hidden;
vertical-align: -0.2rem;
}
.button-icon {
width: 6rem;
height: 3rem;
fill: currentColor;
overflow: hidden;
vertical-align: -3rem;
}
.buy-icon {
width: 10rem;
height: 3rem;
fill: currentColor;
overflow: hidden;
vertical-align: -3rem;
}
}
.mobile-dvdetail-index {
@include full-screen;
@include wh(auto, auto);
background-color: #f8f9fd;
.device-detail-info {
background-color: #efeff7;
padding: 3rem 1rem;
padding-top: 3rem;
.bracelet-icon {
padding-top: 0.4rem;
}
.state-msg {
@include sc($mobile-font-bg, #303030);
// font-weight: bold;
line-height: 2.5rem;
}
.secend-msg {
@include sc($mobile-font-base, #303030);
opacity: 0.8;
line-height: 1.4rem;
text-align: start;
}
}
.mobile-detail-content {
padding: 2rem;
.detail-box-index {
@include border-radius;
@include sc($mobile-font-base, #303030);
// font-weight: bold;
line-height: 2rem;
background-color: #ffffff;
padding: 1rem;
margin-bottom: 1rem;
}
}
}
.mobile-org-box {
@include wh(auto, auto);
@include border-radius;
max-width: 100vw;
padding: 0.5rem;
// margin: 0 2rem;
.device-icon {
margin-top: 0.5rem;
@extend .icon-button-middle;
}
.main-info {
@include sc($mobile-font-base, #000000);
line-height: 2rem;
}
.secend-info {
@include sc($mobile-font-sm, #3e3c36);
text-align: start;
}
.unbind-icon {
@extend .icon-button-middle;
}
}
.mobile-identity-sign {
@include sc($mobile-font-xsm, #d5b870);
line-height: 1.5;
padding: 0 0.3rem;
font-weight: bold;
margin-left: 1rem;
background-color: black;
border-radius: 0.2rem;
}
.mobile-position-mapinfo {
@include sc($mobile-font-md, #303030);
background-color: white;
text-align: start;
.icon {
@extend .icon-small;
vertical-align: -0.2rem;
}
.content {
@include sc($mobile-font-base, #5d9eff);
}
}
.detail-weektime-btn {
color: black;
background: black;
border-radius: 1rem;
background-color: white;
&.active,
&:hover {
background-color: black;
}
}
.mobile-detail-infobox {
.title {
@include sc($mobile-font-base, #303030);
text-align: start;
font-weight: bold;
padding-left: 1rem;
padding-top: 0.5rem;
padding-bottom: 0.1rem;
}
.title-mark {
@include sc($mobile-font-xsm, grey);
padding-bottom: 0.1rem;
opacity: 0.5;
}
.reference {
@include sc($mobile-font-xsm, grey);
text-align: end;
opacity: 0.5;
padding-right: 1rem;
padding-bottom: 0.1rem;
}
.eval {
@include sc($mobile-font-sm, #777777);
padding: 1rem;
text-align: start;
background-color: white;
.bigval {
font-size: $mobile-font-bg;
font-weight: bolder;
padding: 0 0.2rem;
}
}
.progress-index {
background-color: #f4f5f5;
padding-top: 0.3rem;
}
}
/////////////////////////////布局 block////////////
.sys-padding-block {
@include wh(auto, 1rem);
}
.layout-title-content {
@include wh(auto, 2rem);
@include sc($mobile-font-sm, #666666);
line-height: 2rem;
padding: 0 $mobile-padding-left;
background-color: #f8f9fd;
text-align: start;
}
.sys-text-left {
text-align: start;
}
.sys-text-right {
text-align: end;
}
.sys-btn-radius {
.van-button {
border-radius: 0.5rem;
}
}
.sys-btn-card-allscreen {
background-color: #f6f6f6;
min-height: 100vh;
padding: 1rem;
width: 100vw;
overflow: hidden;
}
.sys-box-index {
background-color: white;
padding: 1rem;
margin: 0.5rem 0;
line-height: 1.2;
border-radius: 0.2rem;
box-shadow: 0 2px 4px 0 rgb(0 0 0 / 10%);
}
.sys-bottom-border {
padding: 0.5rem 0;
}
.sys-big-title {
@include sc($mobile-font-bger, white);
line-height: 2;
font-weight: bold;
}
.sys-sm-title {
@include sc($mobile-font-sm, white);
line-height: 2;
}
.sys-pc-bgtitle {
@include sc($pc-font-sm, white);
height: $pc-font-md;
line-height: 1.5;
text-align: start;
padding: 0.1rem 0rem 0.1rem 0.4rem;
background-color: #aadcff;
box-shadow: 0 2px 4px 0 rgb(0 0 0 / 10%);
span {
@include sc($pc-font-xsm, white);
}
}
.sys-my-toash {
background-color: #5d9eff;
opacity: 0.1;
}
$progresspadding: 30px;

72
src/styles/common.scss Normal file
View File

@ -0,0 +1,72 @@
body {
-webkit-font-smoothing: antialiased;
}
.title-lg {
@include ft($font-size-lg, $line-height-base);
}
.title-md {
@include ft($font-size-md, $line-height-base);
}
.font-sm {
@include ft($font-size-sm, $line-height-base);
}
.font-bold {
@include wc($font-weight-base, $font-color-primary);
}
.margin {
margin: 0 auto;
}
.left {
float: left;
}
.right {
float: right;
}
.hide {
display: none;
}
.show {
display: block;
}
.ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/
::-webkit-scrollbar {
width: 0px;
height: 0px;
background-color: #f5f5f5;
}
/*定义滚动条轨道 内阴影+圆角*/
::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 1px rgba(0, 0, 0, 0);
border-radius: 10px;
background-color: #f5f5f5;
}
/*定义滑块 内阴影+圆角*/
::-webkit-scrollbar-thumb {
border-radius: 10px;
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
background-color: #555;
}
* {
-webkit-touch-callout: none; /*系统默认菜单被禁用*/
-webkit-user-select: none; /*webkit浏览器*/
-khtml-user-select: none; /*早期浏览器*/
-moz-user-select: none; /*火狐*/
-ms-user-select: none; /*IE10*/
user-select: none;
}

View File

199
src/styles/mixin.scss Normal file
View File

@ -0,0 +1,199 @@
//定位全屏
@mixin allcover {
position: absolute;
top: 0;
right: 0;
// top: 0;
// left: 0;
}
// 定位顶部栏
@mixin topcover {
position: fixed;
top: 0;
right: 0;
left: 0;
}
/*
子元素排列
*/
@mixin contentEnd {
position: absolute;
}
/*
划线专区
*/
// 设置下划线
@mixin bottomBoder($width, $color) {
border-bottom: $width $color solid;
}
/*
默认margin
*/
@mixin lrmg {
margin: 0 $line-pc-margin;
}
@mixin margin($tb, $lr) {
margin: $tb $lr;
}
@mixin padding($tb, $lr) {
padding: $tb $lr;
}
// auto适应
@mixin autowh {
width: auto;
height: auto;
max-width: 100%;
max-height: 100%;
}
//transform上下左右居中
@mixin ct {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
//定位上下左右居中
@mixin ctp($width, $height) {
position: absolute;
top: 50%;
left: 50%;
margin-top: -$height/2;
margin-left: -$width/2;
}
//flex上下左右剧中
@mixin fct() {
display: flex;
justify-content: center;
align-items: center;
}
//定位上下居中
@mixin tb {
position: absolute;
top: 50%;
transform: translateY(-50%);
}
//flex上下剧中
@mixin ftb {
display: flex;
align-items: center;
}
//定位左右居中
@mixin lr {
position: absolute;
left: 50%;
transform: translateX(-50%);
}
//flex左右剧中
@mixin flr() {
display: flex;
justify-content: center;
}
//宽高
@mixin wh($width, $height) {
width: $width;
height: $height;
}
//字体大小行高字体
@mixin ft($size, $line-height) {
font-size: $size;
line-height: $line-height;
}
//字体大小颜色
@mixin sc($size, $color) {
font-size: $size;
color: $color;
}
//字体粗细颜色
@mixin wc($weight, $color) {
font-weight: $weight;
color: $color;
}
//flex 布局和 子元素 对其方式
@mixin fj($type: space-between) {
display: flex;
justify-content: $type;
}
//2倍图3倍图默认2倍图
@mixin bg-image($url) {
background-image: url("./images/"+$url+"@2x.png");
@media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-device-pixel-ratio: 2) {
background-image: url("./images/"+$url+"@3x.png");
}
}
//多行超出省略号
@mixin ellipsis($line: 2, $line-height: 1.2) {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: $line;
line-height: $line-height;
}
@mixin modalbg($color-bg: rgba(0, 0, 0, 0.2)) {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: $color-bg;
}
@mixin flex($direction: column, $wrap: nowrap) {
display: flex;
flex-direction: $direction;
flex-wrap: $wrap;
}
@mixin clear() {
&:after {
content: ".";
clear: both;
display: block;
width: 0;
height: 0;
visibility: hidden;
}
}
// 宽高度计算
@mixin m-x($width: 1) {
@if unit($width) ==px {
margin: {
left: $width !important;
right: $width !important;
}
} @else {
margin: {
left: $width * 10px !important;
right: $width * 10px !important;
}
}
}
// 有色box 圆角
@mixin border-radius {
border-radius: 0.3rem;
}
//铺满屏幕
@mixin full-screen {
min-height: 100vh;
max-width: 100vw;
}

18
src/styles/settings.scss Normal file
View File

@ -0,0 +1,18 @@
$mobile-font-mbger: 2rem;
$mobile-font-bger: 1.6rem;
$mobile-font-bg: 1.3rem; //18px
$mobile-font-md: 1.15rem; //16px
$mobile-font-base: 1rem; //14px
$mobile-font-sm: 0.85rem; //12px
$mobile-font-xsm: 0.625rem; //10px
$mobile-font-xxsm: 0.4rem;
$pc-font-bg: 1.3rem;
$pc-font-md: 1.15rem;
$pc-font-base: 1rem; //50px
$pc-font-sm: 0.6rem;
$pc-font-xsm: 0.48rem;
$pc-font-xxsm: 0.32rem;
$pc-font-mini: 0.2rem;

12
src/views/index.vue Normal file
View File

@ -0,0 +1,12 @@
<template>
<div>
hello!
</div>
</template>
<script>
export default {};
</script>
<style>
</style>

View File

@ -0,0 +1,24 @@
<template>
<div></div>
</template>
<script>
export default {
name: "RedirectPage",
created() {
const { params, query } = this.$route;
const { path } = params;
if (path) {
this.$router.replace({ path: `/${path}`, query });
} else {
this.$router.replace({ path: `/`, query });
}
},
render(h) {
return h();
}
};
</script>
<style>
</style>

132
vue.config.js Normal file
View File

@ -0,0 +1,132 @@
const path = require("path");
function resolve(dir) {
return path.join(__dirname, dir);
}
const isProduction = process.env.NODE_ENV === "production";
const EXTERNALS = {
vue: "Vue",
axios: "axios",
"element-ui": "ELEMENT",
"vue-router": "VueRouter",
vuex: "Vuex",
vant: "vant",
// "vuex-persistedstate": "createPersistedState",
lodash: "_",
dayjs: "dayjs",
echarts: "echarts",
};
const CDN_LIST = {
css: ["https://cdn.staticfile.org/element-ui/2.15.1/theme-chalk/index.min.css", "https://cdn.staticfile.org/vant/2.12.13/index.min.css"],
js: [
"https://cdn.staticfile.org/vue/2.6.12/vue.min.js",
"https://cdn.staticfile.org/vue-router/3.5.1/vue-router.min.js",
"https://cdn.staticfile.org/vuex/3.6.2/vuex.min.js",
"https://cdn.staticfile.org/axios/0.21.1/axios.min.js",
"https://cdn.staticfile.org/element-ui/2.15.1/index.min.js",
"https://cdn.staticfile.org/vant/2.12.13/vant.min.js",
// "https://cdn.staticfile.org/vuex-persistedstate/4.0.0-beta.3/vuex-persistedstate.min.js",
"https://cdn.staticfile.org/lodash.js/4.17.21/lodash.min.js",
"https://cdn.staticfile.org/dayjs/1.10.4/dayjs.min.js",
"https://cdn.staticfile.org/echarts/5.1.2/echarts.min.js",
],
};
module.exports = {
publicPath: process.env.VUE_APP_PUBLIC_PATH || "/",
devServer: {
proxy: {
"/apis": {
target: "https://api.yyjishu.com/antifraud",
changeOrigin: true,
pathRewrite: {
"^/apis": "",
},
},
},
publicPath: process.env.VUE_APP_PUBLIC_PATH || "/",
disableHostCheck: !isProduction, // 关闭 host check方便使用 ngrok 之类的内网转发工具
},
chainWebpack: (config) => {
// 设置一些常用alias
config.resolve.alias
.set("@", resolve("src"))
.set("@assets", resolve("./src/assets"))
.set("@components", resolve("src/components"))
.set("@styles", resolve("src/styles"))
.set("@layout", resolve("src/components/common/layout"))
.set("@views", resolve("src/views"));
config.plugins.delete("prefetch");
// 压缩图片
config.module
.rule("images")
.use("image-webpack-loader")
.loader("image-webpack-loader")
.options({ bypassOnDebug: true })
.end();
if (isProduction) {
// 通过 html-webpack-plugin 将 cdn 注入到 index.html 之中
config.externals(EXTERNALS);
config.plugin("html").tap((args) => {
args[0].cdn = CDN_LIST;
return args;
});
}
},
configureWebpack: (config) => {
if (isProduction) {
// 公共代码抽离
config.optimization = {
splitChunks: {
cacheGroups: {
vendor: {
chunks: "all",
test: /node_modules/,
name: "vendor",
minChunks: 1,
maxInitialRequests: 5,
minSize: 0,
priority: 100,
},
common: {
chunks: "all",
test: /[\\/]src[\\/]js[\\/]/,
name: "common",
minChunks: 2,
maxInitialRequests: 5,
minSize: 0,
priority: 60,
},
styles: {
name: "styles",
test: /\.(sa|sc|c)ss$/,
chunks: "all",
enforce: true,
},
runtimeChunk: {
name: "manifest",
},
},
},
};
}
},
css: {
loaderOptions: {
scss: {
additionalData: `
@import "./src/styles/mixin";
@import "./src/styles/_var";
@import "./src/styles/all.scss";`,
},
postcss: {
plugins: [require("postcss-px2rem")({ remUnit: 32 })],
},
},
},
productionSourceMap: false,
};