This commit is contained in:
2025-08-14 11:50:11 +08:00
commit d488db04bc
242 changed files with 34863 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
### 该问题是怎么引起的?
### 重现步骤
### 报错信息

View File

@@ -0,0 +1,14 @@
### 相关的Issue
### 原因(目的、解决的问题等)
### 描述(做了什么,变更了什么)
### 测试用例(新增、改动、可能影响的功能)

7
.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
/unpackage/dist/*
/unpackage/cache/*
/unpackage/release/*
/node_modules/*
/.hbuilderx/*
/.idea/*
deploy.sh

31
App.vue Normal file
View File

@@ -0,0 +1,31 @@
<script>
/**
* Copyright (c) 2013-Now http://aidex.vip All rights reserved.
*/
export default {
onLaunch() {
// 国际化,设置当前语言
if (this.vuex_locale) {
this.$i18n.locale = this.vuex_locale;
this.$u.api.lang({
lang: this.vuex_locale
});
}
// 设置底部导航栏角标
uni.setTabBarBadge({
index: 0,
text: '3'
});
// uni.removeTabBarBadge({
// index: 0
// });
}
}
</script>
<style>
@import url("~@/static/iconfont/iconfont.css");
</style>
<style lang="scss">
@import "uview-ui/index.scss";
@import "pages/common/aidex.scss";
</style>

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 皮皮大刺猬
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

179
README.md Normal file
View File

@@ -0,0 +1,179 @@
# RuoYi-Uniapp若依-手机端)开源啦
#### 介绍
&nbsp; &nbsp; 若依-Ruoyi APP 移动解决方案,基于 uniapp+uView 封装的一套基础模版开箱即用一份代码多终端适配支持H5+支付宝小程序+微信小程序+APP实现了与ruoyi-vue后台完美对接的移动解决方案可直接开始快速开发业务需求全新UI设计更多交互细节我们将为您提供极致的交互体验体验持续推出高质量的交互产品。
如果对您有帮助,您可以点右上角 “Star” 收藏一下 获取第一时间更新谢谢刚刚开源BUG修复中
* 感谢jeesite项目参考自[JeeSite Mobile Uni-App](https://gitee.com/thinkgem/jeesite4-uniapp)
* 感谢[RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue)
* 适配ruoyi-vue后端将doc下的java类放进去即可
#### 快速体验
2、微信小程序端扫码访问目前只能用用户名密码方式登录用户名admin 密码admin123<br><br>
<img src="https://images.gitee.com/uploads/images/2021/1115/214722_20aaf4c8_9700683.jpeg" width="220" height="220" >
## 基于RuoYi修改的美化皮肤的样式地址
- [🎉 RuoYi + vue2.x + Max + element-uivue2.x 支持 PC、平板、手机](http://82.157.44.212:8091/index)
- [🎉 RuoYi + vue3.x + Max + element-plusvue3.x 支持 PC、平板、手机](http://82.157.44.212:8090/index)
- [🎉 RuoYi + vue2.x + Max + element-ui + Cloudvue2.x 支持 PC、平板、手机](http://82.157.44.212:8093/index)
- [🎉 RuoYi + vue3.x + Max + element-plus + Cloudvue3.x 支持 PC、平板、手机](http://82.157.44.212:8092/index)
- [🎉 RuoYi + vue3.x + element-plus + uniapp2vue3.x 支持 PC、平板、手机](http://82.157.44.212:8094/#/)
#### 我的另一个项目:
**AiDex Sharp 快速开发平台** 基于著名的开源项目“ **若依-RuoYi-Vue** ”改造而成,追求 **极致的UI交互体验****快速开发** ,一切向 **效率** 看齐, **重构优化** 后端的代码,对前端页面进行了 **美化****我们将持续升级,持续完善,欢迎友友们收藏和点赞**
* [打开Aidex Sharp](https://gitee.com/big-hedgehog/aidex-sharp)
<table>
<tr>
<td><img src="https://images.gitee.com/uploads/images/2021/0923/234748_170e4ee7_9700683.png"/></td>
<td><img src="https://images.gitee.com/uploads/images/2021/0911/184210_ffa2880b_9700683.png"/></td>
</tr>
</table>
#### 如何使用uni-app端
##### 一、导入uniapp项目
1. 首先下载HBuilderX并安装地址https://www.dcloud.io/hbuilderx.html
2. 打开HBuilderX -> 顶部菜单栏 -> 文件 -> 导入 -> 从本地目录导入 -> 选择uniapp端项目目录
3. 找到common/config.js文件找到里面的apiUrl项填入已搭建的后端url地址
4. 打开manifest.json文件选择微信小程序配置填写小程序的appid
##### 二、本地调试
1. 打开HBuilderX -> 顶部菜单栏 -> 运行 -> 运行到浏览器 -> Chrome
2. 如果请求后端api时 提示跨域错误可安装Chrome插件【Allow CORS: Access-Control-Allow-Origin】地址https://chrome.google.com/webstore/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf
##### 三、打包发行H5
1. 打开HBuilderX -> 顶部菜单栏 -> 发行 -> 网站H5-手机版
2. 打包后的文件路径:/unpackage/dist/build/h5
3. 将打包完成的所有文件 复制到商城后端/pulic目录下全部替换
##### 四、打包发行(微信小程序)
1. 下载微信开发者工具并安装地址https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html
2. 打开HBuilderX -> 顶部菜单栏 -> 发行 -> 小程序-微信
3. 打包后的文件路径:/unpackage/dist/build/mp-weixin
5. 打开微信开发者工具 导入 打包完成的项目
6. 检查没有运行错误,在右上方上传小程序
##### 5、后端代码适配ruoyi-vue
1. 可以启动后端直接访问http://aidex.vip的公共服务如果要自己适配请将doc目录下的代码放到项目中即可。
#### 界面截图
<table>
<tr>
<td style="border:5px"><img src="https://images.gitee.com/uploads/images/2021/1112/184344_b519b98b_9700683.png" width="300" height="480"/></td>
<td style="border:5px"><img src="https://images.gitee.com/uploads/images/2021/1112/223144_43bc09a8_9700683.png" width="300" height="480"/></td>
<td style="border:5px"><img src="https://images.gitee.com/uploads/images/2021/1112/223205_fd09aab2_9700683.png" width="300" height="480"/></td>
</tr>
<tr>
<td style="border:5px"><img src="https://images.gitee.com/uploads/images/2021/1112/223528_bd934103_9700683.png" width="300" height="480"/></td>
<td style="border:5px"><img src="https://images.gitee.com/uploads/images/2021/1112/223553_5d4f27a1_9700683.png" width="300" height="480"/></td>
<td style="border:5px"><img src="https://images.gitee.com/uploads/images/2021/1112/223612_8ca07db6_9700683.png" width="300" height="480"/></td>
</tr>
<tr>
<td style="border:5px"><img src="https://images.gitee.com/uploads/images/2021/1112/223248_7d40c45c_9700683.png" width="300" height="480"/></td>
<td style="border:5px"><img src="https://images.gitee.com/uploads/images/2021/1112/223306_a6be218d_9700683.png" width="300" height="480"/></td>
<td style="border:5px"><img src="https://images.gitee.com/uploads/images/2021/1112/223324_a3415319_9700683.png" width="300" height="480"/></td>
</tr>
<tr>
<td style="border:5px"><img src="https://images.gitee.com/uploads/images/2021/1112/223405_a7fd6593_9700683.png" width="300" height="480"/></td>
<td style="border:5px"><img src="https://images.gitee.com/uploads/images/2021/1112/223424_398ebcde_9700683.png" width="300" height="480"/></td>
<td style="border:5px"><img src="https://images.gitee.com/uploads/images/2021/1112/223501_db695cd4_9700683.png" width="300" height="480"/></td>
</tr>
</table>
## 我的另一个项目:
**AiDex Sharp 快速开发平台** 基于著名的开源项目“ **若依-RuoYi-Vue** ”改造而成,追求 **极致的UI交互体验****快速开发** ,一切向 **效率** 看齐, **重构优化** 后端的代码,对前端页面进行了 **美化****我们将持续升级,持续完善,欢迎友友们收藏和点赞**
* [打开Aidex Sharp](https://gitee.com/big-hedgehog/aidex-sharp)
官方QQ群
Aidex Sharp快速开发平台3群 208511180 使用问题请入群由专人负责简答
## 后台系统截图
<table>
<tr>
<td><img src="https://images.gitee.com/uploads/images/2021/0923/234823_7d05456a_9700683.png"/></td>
<td><img src="https://images.gitee.com/uploads/images/2021/0923/234748_170e4ee7_9700683.png"/></td>
</tr>
<tr>
<td><img src="https://images.gitee.com/uploads/images/2021/0911/184041_c4d1f1aa_9700683.png"/></td>
<td><img src="https://images.gitee.com/uploads/images/2021/0911/184055_0cf08e45_9700683.png"/></td>
</tr>
<tr>
<td><img src="https://images.gitee.com/uploads/images/2021/0911/184110_2e6df64f_9700683.png"/></td>
<td><img src="https://images.gitee.com/uploads/images/2021/0911/184125_3d5bdddf_9700683.png"/></td>
</tr>
<tr>
<td><img src="https://images.gitee.com/uploads/images/2021/0911/184139_092a8f07_9700683.png"/></td>
<td><img src="https://images.gitee.com/uploads/images/2021/0922/225255_f8710fb3_9700683.png"/></td>
</tr>
<tr>
<td><img src="https://images.gitee.com/uploads/images/2021/0911/184210_ffa2880b_9700683.png"/></td>
<td><img src="https://images.gitee.com/uploads/images/2021/0911/184223_8f57f5f0_9700683.png"/></td>
</tr>
<tr>
<td><img src="https://images.gitee.com/uploads/images/2021/0911/184238_5cb3e09e_9700683.png"/></td>
<td><img src="https://images.gitee.com/uploads/images/2021/0911/184256_5bc77bff_9700683.png"/></td>
</tr>
</table>
更多功能请访问系统体验
## 在线体验
演示地址http://aidex.vip 帐号admin 密码admin123
#### uniapp知识
1. <a href="https://uniapp.dcloud.io/README" target="blank">uni-app介绍</a>
2. <a href="https://ke.qq.com/course/3169971" target="blank">uni-app 官方视频教程</a>
3. <a href="https://www.dcloud.io/hbuilderx.html" target="blank">uni-app开发工具 HBuilderX 下载及使用说明</a>
4. <a href="http://ask.dcloud.net.cn/article/35657" target="blank">uni-app是什么能解决什么问题</a>
5. <a href="https://uniapp.dcloud.io/vue-basics" target="blank">Vue.js相关文档、视频教程</a>
#### 技术手册
* <a href="https://uniapp.dcloud.io/collocation/pages" target="blank">uni-app 框架文档</a>
* <a href="https://uniapp.dcloud.io/component/README" target="blank">uni-app 组件文档</a>
* <a href="https://uviewui.com/components/intro.html" target="blank">uView 组件文档</a>
* <a href="https://uviewui.com/js/intro.html" target="blank">uView JS 文档</a>
#### 授权许可协议条款
1. Ruoyi-Uniapp采用MIT开源协议协议。
2. 代码可用于个人项目等接私活或企业项目脚手架使用Ruoyi-Uniapp开源版完全免费。
3. 允许进行商用,但是不允许二次开源出来并进行收费,否则将追究侵权者法律责任。
4. 请不要删除和修改Ruoyi-Uniapp源码头部的版权与作者声明及出处。
5. 不得进行简单修改包装声称是自己的项目。
6. 我们已经申请了相关的软件开发著作权和相关登记
7. 需要在您的软件介绍明显位置说明出处举例本软件基于Ruoyi-Uniapp手机端
#### 关于我们
&nbsp; &nbsp; 我们擅长UI、前端开发、后端架构有一颗热爱开源的心致力于打造企业级的通用产品设计UI体系让项目
或者更直观更高效、更简单未来将持续关注UI交互持续推出高质量的交互产品。
######
<img src="https://images.gitee.com/uploads/images/2021/1112/114326_5eb079c2_9700683.jpeg" width="220" height="220" >&nbsp; &nbsp; &nbsp;&nbsp;
<img src="https://images.gitee.com/uploads/images/2021/1112/114207_bb1bac92_9700683.jpeg" width="220" height="220" >&nbsp; &nbsp; &nbsp;&nbsp;
<img src="https://images.gitee.com/uploads/images/2021/1115/164243_d4e0d61d_9700683.png" width="220" height="220" >&nbsp; &nbsp; &nbsp;&nbsp;
版权所有Copyright © 2017-2021 By AiDex (http://www.aidex.vip) All rights reserved

74
common/base64.js Normal file
View File

@@ -0,0 +1,74 @@
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define([], function() {factory(root);});
} else factory(root);
// node.js has always supported base64 conversions, while browsers that support
// web workers support base64 too, but you may never know.
})(typeof exports !== "undefined" ? exports : this, function(root) {
if (root.atob) {
// Some browsers' implementation of atob doesn't support whitespaces
// in the encoded string (notably, IE). This wraps the native atob
// in a function that strips the whitespaces.
// The original function can be retrieved in atob.original
try {
root.atob(" ");
} catch(e) {
root.atob = (function(atob) {
var func = function(string) {
return atob(String(string).replace(/[\t\n\f\r ]+/g, ""));
};
func.original = atob;
return func;
})(root.atob);
}
return;
}
// base64 character set, plus padding character (=)
var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
// Regular expression to check formal correctness of base64 encoded strings
b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/;
root.btoa = function(string) {
string = String(string);
var bitmap, a, b, c,
result = "", i = 0,
rest = string.length % 3; // To determine the final padding
for (; i < string.length;) {
if ((a = string.charCodeAt(i++)) > 255
|| (b = string.charCodeAt(i++)) > 255
|| (c = string.charCodeAt(i++)) > 255)
throw new TypeError("Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.");
bitmap = (a << 16) | (b << 8) | c;
result += b64.charAt(bitmap >> 18 & 63) + b64.charAt(bitmap >> 12 & 63)
+ b64.charAt(bitmap >> 6 & 63) + b64.charAt(bitmap & 63);
}
// If there's need of padding, replace the last 'A's with equal signs
return rest ? result.slice(0, rest - 3) + "===".substring(rest) : result;
};
root.atob = function(string) {
// atob can work with strings with whitespaces, even inside the encoded part,
// but only \t, \n, \f, \r and ' ', which can be stripped.
string = String(string).replace(/[\t\n\f\r ]+/g, "");
if (!b64re.test(string))
throw new TypeError("Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.");
// Adding the padding if missing, for semplicity
string += "==".slice(2 - (string.length & 3));
var bitmap, result = "", r1, r2, i = 0;
for (; i < string.length;) {
bitmap = b64.indexOf(string.charAt(i++)) << 18 | b64.indexOf(string.charAt(i++)) << 12
| (r1 = b64.indexOf(string.charAt(i++))) << 6 | (r2 = b64.indexOf(string.charAt(i++)));
result += r1 === 64 ? String.fromCharCode(bitmap >> 16 & 255)
: r2 === 64 ? String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255)
: String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255, bitmap & 255);
}
return result;
};
});

15
common/common.js Normal file
View File

@@ -0,0 +1,15 @@
/**
* 通用js方法封装处理
* Copyright (c) 2019 aidex
*/
export function replaceAll (text,stringToFind,stringToReplace) {
if ( stringToFind == stringToReplace) return this;
var temp = text;
var index = temp.indexOf(stringToFind);
while (index != -1) {
temp = temp.replace(stringToFind, stringToReplace);
index = temp.indexOf(stringToFind);
}
return temp;
}

30
common/config.js Normal file
View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2013-Now http://aidex.vip All rights reserved.
*/
const config = {
// 产品名称
productName: 'Aidex Mobile',
// 公司名称
companyName: 'Aidex',
// 产品版本号
productVersion: 'V4.3.0',
// 版本检查标识
appCode: 'android',
// 内部版本号码
appVersion: 1,
// 管理基础路径
adminPath: ''
}
// 设置后台接口服务的基础地址
// config.baseUrl = 'http://tc.cqsznc.com:7080';
// config.baseUrl = 'http://192.168.71.139:8080';
config.baseUrl = '/api';
export default config;

23
common/http.api.js Normal file
View File

@@ -0,0 +1,23 @@
/**
* Copyright (c) 2013-Now http://aidex.vip All rights reserved.
*/
// 此处第二个参数vm就是我们在页面使用的this你可以通过vm获取vuex等操作
const install = (Vue, vm) => {
// 参数配置对象
const config = vm.vuex_config;
// 将各个定义的接口名称统一放进对象挂载到vm.$u.api(因为vm就是this也即this.$u.api)下
vm.$u.api = {
codesub: (params = {}) => vm.$u.get(config.adminPath+'/property/visitorManagement/useqr', params),
uploadimg: (params = {}) => vm.$u.post(config.adminPath+'/resource/oss/upload', params),
fksub: (params = {}) => vm.$u.post(config.adminPath+'/property/visitorManagement/add', params),
login: (params = {}) => vm.$u.post(config.adminPath+'/auth/login', params),
getUserInfo: (params = {}) => vm.$u.get(config.adminPath+'/system/user/profile', params)
};
}
export default {
install
}

119
common/http.interceptor.js Normal file
View File

@@ -0,0 +1,119 @@
/**
* Copyright (c) 2013-Now http://aidex.vip All rights reserved.
*/
// 此处第二个参数vm就是我们在页面使用的this你可以通过vm获取vuex等操作
const install = (Vue, vm) => {
// 通用请求头设定
const ajaxHeader = 'x-ajax';
const sessionIdHeader = 'Authorization';
const rememberMeHeader = 'x-remember';
// 请求参数默认配置
Vue.prototype.$u.http.setConfig({
baseUrl: vm.vuex_config.baseUrl,
originalData: true,
// 默认头部http2约定header名称统一小写 aidex
header: {
'content-type': 'application/json',
'x-requested-with': 'XMLHttpRequest'
}
});
// 请求拦截配置Token等参数
Vue.prototype.$u.http.interceptor.request = (req) => {
if (!req.header){
req.header = [];
}
req.header["source"] = "uniapp";
req.header["clientId"] = "dab457a1ea14411787c240db05bb0832"
// 默认指定返回 JSON 数据
if (!req.header[ajaxHeader]){
req.header[ajaxHeader] = 'json';
}
// 设定传递 Token 认证参数 aidex
if (req.url!="/auth/login"&&!req.header[sessionIdHeader] && vm.vuex_token){
req.header[sessionIdHeader] = "Bearer "+vm.vuex_token;
}
// 为节省流量,记住我数据不是每次都发送的,当会话失效后,尝试重试登录 aidex
if (!req.header[rememberMeHeader] && vm.vuex_remember && req.remember){
req.header[rememberMeHeader] = vm.vuex_remember;
req.remember = false;
}
console.log('request', req);
return req;
}
// 响应拦截,判断状态码是否通过
Vue.prototype.$u.http.interceptor.response = async (res, req) => {
console.log('response', res);
let data = res.data;
if (!(data)){
vm.$u.toast('未连接到服务器')
return false;
}
if (typeof data === 'object' && !(data instanceof Array)){
if (data.token){
vm.$u.vuex('vuex_token', data.token);
if (data.user){
vm.$u.vuex('vuex_user', data.user);
}
vm.$u.vuex('vuex_isAgent', data.isAgent);
}
if (data.result === 'login'){
vm.$u.vuex('vuex_user', {});
if (req.tryagain == undefined || req.tryagain){
req.tryagain = false; req.remember = true;
await vm.$u.http.request(req).then(res => {
data = res;
});
}
if (data.result === 'login'){
if (!req.data.loginCheck){
vm.$u.toast(data.msg);
}
req.tryagain = true;
}
}
}
if (res.header && res.header[rememberMeHeader]){
let remember = res.header[rememberMeHeader];
if (remember && remember != 'deleteMe'){
vm.$u.vuex('vuex_remember', remember);
}else{
vm.$u.vuex('vuex_remember', '');
}
}
return data;
}
// 封装 get text 请求
vm.$u.getText = (url, data = {}, header = {}) => {
return vm.$u.http.request({
dataType: 'text',
method: 'GET',
url,
header,
data
})
}
// 封装 post json 请求
vm.$u.postJson = (url, data = {}, header = {}) => {
header['content-type'] = 'application/json';
return vm.$u.http.request({
url,
method: 'POST',
header,
data
})
}
}
export default {
install
}

34
common/locales/en.js Normal file
View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2013-Now http://aidex.vip All rights reserved.
*/
export default {
common: {
title: 'Aidex',
},
nav: {
home: 'Home',
user: 'User',
msg: 'Msg'
},
login: {
title: 'Login',
placeholderAccount: 'Enter Account',
placeholderPassword: 'Enter Password',
autoLogin: 'Auto Login',
loginButton: 'Login',
logoutButton: 'Logout',
forget: 'Forget Password',
reg: 'Resister Account',
noLogin: 'No Login'
},
home: {
title: 'Home'
},
user: {
title: 'User'
},
msg: {
title: 'Message'
}
}

41
common/locales/zh_CN.js Normal file
View File

@@ -0,0 +1,41 @@
/**
* Copyright (c) 2013-Now http://aidex.vip All rights reserved.
*/
export default {
common: {
title: 'Aidex',
},
nav: {
home: '消息',
workbench: '工作台',
user: '我的',
book: '通讯录',
msg: '消息',
},
login: {
title: '登录',
placeholderAccount: '请输入账号',
placeholderPassword: '请输入密码',
autoLogin: '自动登录',
loginButton: '登录',
logoutButton: '退出登录',
forget: '忘记密码',
reg: '注册账号',
noLogin: '未登录'
},
home: {
title: '消息'
},
workbench: {
title: '工作台'
},
user: {
title: '用户中心'
},
msg: {
title: '消息'
},
book: {
title: '通讯录'
}
}

751
common/spark-md5.js Normal file
View File

@@ -0,0 +1,751 @@
(function (factory) {
if (typeof exports === 'object') {
// Node/CommonJS
module.exports = factory();
} else if (typeof define === 'function' && define.amd) {
// AMD
define(factory);
} else {
// Browser globals (with support for web workers)
var glob;
try {
glob = window;
} catch (e) {
glob = self;
}
glob.SparkMD5 = factory();
}
}(function (undefined) {
'use strict';
/*
* Fastest md5 implementation around (JKM md5).
* Credits: Joseph Myers
*
* @see http://www.myersdaily.org/joseph/javascript/md5-text.html
* @see http://jsperf.com/md5-shootout/7
*/
/* this function is much faster,
so if possible we use it. Some IEs
are the only ones I know of that
need the idiotic second function,
generated by an if clause. */
var add32 = function (a, b) {
return (a + b) & 0xFFFFFFFF;
},
hex_chr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
function cmn(q, a, b, x, s, t) {
a = add32(add32(a, q), add32(x, t));
return add32((a << s) | (a >>> (32 - s)), b);
}
function md5cycle(x, k) {
var a = x[0],
b = x[1],
c = x[2],
d = x[3];
a += (b & c | ~b & d) + k[0] - 680876936 | 0;
a = (a << 7 | a >>> 25) + b | 0;
d += (a & b | ~a & c) + k[1] - 389564586 | 0;
d = (d << 12 | d >>> 20) + a | 0;
c += (d & a | ~d & b) + k[2] + 606105819 | 0;
c = (c << 17 | c >>> 15) + d | 0;
b += (c & d | ~c & a) + k[3] - 1044525330 | 0;
b = (b << 22 | b >>> 10) + c | 0;
a += (b & c | ~b & d) + k[4] - 176418897 | 0;
a = (a << 7 | a >>> 25) + b | 0;
d += (a & b | ~a & c) + k[5] + 1200080426 | 0;
d = (d << 12 | d >>> 20) + a | 0;
c += (d & a | ~d & b) + k[6] - 1473231341 | 0;
c = (c << 17 | c >>> 15) + d | 0;
b += (c & d | ~c & a) + k[7] - 45705983 | 0;
b = (b << 22 | b >>> 10) + c | 0;
a += (b & c | ~b & d) + k[8] + 1770035416 | 0;
a = (a << 7 | a >>> 25) + b | 0;
d += (a & b | ~a & c) + k[9] - 1958414417 | 0;
d = (d << 12 | d >>> 20) + a | 0;
c += (d & a | ~d & b) + k[10] - 42063 | 0;
c = (c << 17 | c >>> 15) + d | 0;
b += (c & d | ~c & a) + k[11] - 1990404162 | 0;
b = (b << 22 | b >>> 10) + c | 0;
a += (b & c | ~b & d) + k[12] + 1804603682 | 0;
a = (a << 7 | a >>> 25) + b | 0;
d += (a & b | ~a & c) + k[13] - 40341101 | 0;
d = (d << 12 | d >>> 20) + a | 0;
c += (d & a | ~d & b) + k[14] - 1502002290 | 0;
c = (c << 17 | c >>> 15) + d | 0;
b += (c & d | ~c & a) + k[15] + 1236535329 | 0;
b = (b << 22 | b >>> 10) + c | 0;
a += (b & d | c & ~d) + k[1] - 165796510 | 0;
a = (a << 5 | a >>> 27) + b | 0;
d += (a & c | b & ~c) + k[6] - 1069501632 | 0;
d = (d << 9 | d >>> 23) + a | 0;
c += (d & b | a & ~b) + k[11] + 643717713 | 0;
c = (c << 14 | c >>> 18) + d | 0;
b += (c & a | d & ~a) + k[0] - 373897302 | 0;
b = (b << 20 | b >>> 12) + c | 0;
a += (b & d | c & ~d) + k[5] - 701558691 | 0;
a = (a << 5 | a >>> 27) + b | 0;
d += (a & c | b & ~c) + k[10] + 38016083 | 0;
d = (d << 9 | d >>> 23) + a | 0;
c += (d & b | a & ~b) + k[15] - 660478335 | 0;
c = (c << 14 | c >>> 18) + d | 0;
b += (c & a | d & ~a) + k[4] - 405537848 | 0;
b = (b << 20 | b >>> 12) + c | 0;
a += (b & d | c & ~d) + k[9] + 568446438 | 0;
a = (a << 5 | a >>> 27) + b | 0;
d += (a & c | b & ~c) + k[14] - 1019803690 | 0;
d = (d << 9 | d >>> 23) + a | 0;
c += (d & b | a & ~b) + k[3] - 187363961 | 0;
c = (c << 14 | c >>> 18) + d | 0;
b += (c & a | d & ~a) + k[8] + 1163531501 | 0;
b = (b << 20 | b >>> 12) + c | 0;
a += (b & d | c & ~d) + k[13] - 1444681467 | 0;
a = (a << 5 | a >>> 27) + b | 0;
d += (a & c | b & ~c) + k[2] - 51403784 | 0;
d = (d << 9 | d >>> 23) + a | 0;
c += (d & b | a & ~b) + k[7] + 1735328473 | 0;
c = (c << 14 | c >>> 18) + d | 0;
b += (c & a | d & ~a) + k[12] - 1926607734 | 0;
b = (b << 20 | b >>> 12) + c | 0;
a += (b ^ c ^ d) + k[5] - 378558 | 0;
a = (a << 4 | a >>> 28) + b | 0;
d += (a ^ b ^ c) + k[8] - 2022574463 | 0;
d = (d << 11 | d >>> 21) + a | 0;
c += (d ^ a ^ b) + k[11] + 1839030562 | 0;
c = (c << 16 | c >>> 16) + d | 0;
b += (c ^ d ^ a) + k[14] - 35309556 | 0;
b = (b << 23 | b >>> 9) + c | 0;
a += (b ^ c ^ d) + k[1] - 1530992060 | 0;
a = (a << 4 | a >>> 28) + b | 0;
d += (a ^ b ^ c) + k[4] + 1272893353 | 0;
d = (d << 11 | d >>> 21) + a | 0;
c += (d ^ a ^ b) + k[7] - 155497632 | 0;
c = (c << 16 | c >>> 16) + d | 0;
b += (c ^ d ^ a) + k[10] - 1094730640 | 0;
b = (b << 23 | b >>> 9) + c | 0;
a += (b ^ c ^ d) + k[13] + 681279174 | 0;
a = (a << 4 | a >>> 28) + b | 0;
d += (a ^ b ^ c) + k[0] - 358537222 | 0;
d = (d << 11 | d >>> 21) + a | 0;
c += (d ^ a ^ b) + k[3] - 722521979 | 0;
c = (c << 16 | c >>> 16) + d | 0;
b += (c ^ d ^ a) + k[6] + 76029189 | 0;
b = (b << 23 | b >>> 9) + c | 0;
a += (b ^ c ^ d) + k[9] - 640364487 | 0;
a = (a << 4 | a >>> 28) + b | 0;
d += (a ^ b ^ c) + k[12] - 421815835 | 0;
d = (d << 11 | d >>> 21) + a | 0;
c += (d ^ a ^ b) + k[15] + 530742520 | 0;
c = (c << 16 | c >>> 16) + d | 0;
b += (c ^ d ^ a) + k[2] - 995338651 | 0;
b = (b << 23 | b >>> 9) + c | 0;
a += (c ^ (b | ~d)) + k[0] - 198630844 | 0;
a = (a << 6 | a >>> 26) + b | 0;
d += (b ^ (a | ~c)) + k[7] + 1126891415 | 0;
d = (d << 10 | d >>> 22) + a | 0;
c += (a ^ (d | ~b)) + k[14] - 1416354905 | 0;
c = (c << 15 | c >>> 17) + d | 0;
b += (d ^ (c | ~a)) + k[5] - 57434055 | 0;
b = (b << 21 |b >>> 11) + c | 0;
a += (c ^ (b | ~d)) + k[12] + 1700485571 | 0;
a = (a << 6 | a >>> 26) + b | 0;
d += (b ^ (a | ~c)) + k[3] - 1894986606 | 0;
d = (d << 10 | d >>> 22) + a | 0;
c += (a ^ (d | ~b)) + k[10] - 1051523 | 0;
c = (c << 15 | c >>> 17) + d | 0;
b += (d ^ (c | ~a)) + k[1] - 2054922799 | 0;
b = (b << 21 |b >>> 11) + c | 0;
a += (c ^ (b | ~d)) + k[8] + 1873313359 | 0;
a = (a << 6 | a >>> 26) + b | 0;
d += (b ^ (a | ~c)) + k[15] - 30611744 | 0;
d = (d << 10 | d >>> 22) + a | 0;
c += (a ^ (d | ~b)) + k[6] - 1560198380 | 0;
c = (c << 15 | c >>> 17) + d | 0;
b += (d ^ (c | ~a)) + k[13] + 1309151649 | 0;
b = (b << 21 |b >>> 11) + c | 0;
a += (c ^ (b | ~d)) + k[4] - 145523070 | 0;
a = (a << 6 | a >>> 26) + b | 0;
d += (b ^ (a | ~c)) + k[11] - 1120210379 | 0;
d = (d << 10 | d >>> 22) + a | 0;
c += (a ^ (d | ~b)) + k[2] + 718787259 | 0;
c = (c << 15 | c >>> 17) + d | 0;
b += (d ^ (c | ~a)) + k[9] - 343485551 | 0;
b = (b << 21 | b >>> 11) + c | 0;
x[0] = a + x[0] | 0;
x[1] = b + x[1] | 0;
x[2] = c + x[2] | 0;
x[3] = d + x[3] | 0;
}
function md5blk(s) {
var md5blks = [],
i; /* Andy King said do it this way. */
for (i = 0; i < 64; i += 4) {
md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);
}
return md5blks;
}
function md5blk_array(a) {
var md5blks = [],
i; /* Andy King said do it this way. */
for (i = 0; i < 64; i += 4) {
md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24);
}
return md5blks;
}
function md51(s) {
var n = s.length,
state = [1732584193, -271733879, -1732584194, 271733878],
i,
length,
tail,
tmp,
lo,
hi;
for (i = 64; i <= n; i += 64) {
md5cycle(state, md5blk(s.substring(i - 64, i)));
}
s = s.substring(i - 64);
length = s.length;
tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
for (i = 0; i < length; i += 1) {
tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);
}
tail[i >> 2] |= 0x80 << ((i % 4) << 3);
if (i > 55) {
md5cycle(state, tail);
for (i = 0; i < 16; i += 1) {
tail[i] = 0;
}
}
// Beware that the final length might not fit in 32 bits so we take care of that
tmp = n * 8;
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
lo = parseInt(tmp[2], 16);
hi = parseInt(tmp[1], 16) || 0;
tail[14] = lo;
tail[15] = hi;
md5cycle(state, tail);
return state;
}
function md51_array(a) {
var n = a.length,
state = [1732584193, -271733879, -1732584194, 271733878],
i,
length,
tail,
tmp,
lo,
hi;
for (i = 64; i <= n; i += 64) {
md5cycle(state, md5blk_array(a.subarray(i - 64, i)));
}
// Not sure if it is a bug, however IE10 will always produce a sub array of length 1
// containing the last element of the parent array if the sub array specified starts
// beyond the length of the parent array - weird.
// https://connect.microsoft.com/IE/feedback/details/771452/typed-array-subarray-issue
a = (i - 64) < n ? a.subarray(i - 64) : new Uint8Array(0);
length = a.length;
tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
for (i = 0; i < length; i += 1) {
tail[i >> 2] |= a[i] << ((i % 4) << 3);
}
tail[i >> 2] |= 0x80 << ((i % 4) << 3);
if (i > 55) {
md5cycle(state, tail);
for (i = 0; i < 16; i += 1) {
tail[i] = 0;
}
}
// Beware that the final length might not fit in 32 bits so we take care of that
tmp = n * 8;
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
lo = parseInt(tmp[2], 16);
hi = parseInt(tmp[1], 16) || 0;
tail[14] = lo;
tail[15] = hi;
md5cycle(state, tail);
return state;
}
function rhex(n) {
var s = '',
j;
for (j = 0; j < 4; j += 1) {
s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F];
}
return s;
}
function hex(x) {
var i;
for (i = 0; i < x.length; i += 1) {
x[i] = rhex(x[i]);
}
return x.join('');
}
// In some cases the fast add32 function cannot be used..
if (hex(md51('hello')) !== '5d41402abc4b2a76b9719d911017c592') {
add32 = function (x, y) {
var lsw = (x & 0xFFFF) + (y & 0xFFFF),
msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
};
}
// ---------------------------------------------------
/**
* ArrayBuffer slice polyfill.
*
* @see https://github.com/ttaubert/node-arraybuffer-slice
*/
if (typeof ArrayBuffer !== 'undefined' && !ArrayBuffer.prototype.slice) {
(function () {
function clamp(val, length) {
val = (val | 0) || 0;
if (val < 0) {
return Math.max(val + length, 0);
}
return Math.min(val, length);
}
ArrayBuffer.prototype.slice = function (from, to) {
var length = this.byteLength,
begin = clamp(from, length),
end = length,
num,
target,
targetArray,
sourceArray;
if (to !== undefined) {
end = clamp(to, length);
}
if (begin > end) {
return new ArrayBuffer(0);
}
num = end - begin;
target = new ArrayBuffer(num);
targetArray = new Uint8Array(target);
sourceArray = new Uint8Array(this, begin, num);
targetArray.set(sourceArray);
return target;
};
})();
}
// ---------------------------------------------------
/**
* Helpers.
*/
function toUtf8(str) {
if (/[\u0080-\uFFFF]/.test(str)) {
str = unescape(encodeURIComponent(str));
}
return str;
}
function utf8Str2ArrayBuffer(str, returnUInt8Array) {
var length = str.length,
buff = new ArrayBuffer(length),
arr = new Uint8Array(buff),
i;
for (i = 0; i < length; i += 1) {
arr[i] = str.charCodeAt(i);
}
return returnUInt8Array ? arr : buff;
}
function arrayBuffer2Utf8Str(buff) {
return String.fromCharCode.apply(null, new Uint8Array(buff));
}
function concatenateArrayBuffers(first, second, returnUInt8Array) {
var result = new Uint8Array(first.byteLength + second.byteLength);
result.set(new Uint8Array(first));
result.set(new Uint8Array(second), first.byteLength);
return returnUInt8Array ? result : result.buffer;
}
function hexToBinaryString(hex) {
var bytes = [],
length = hex.length,
x;
for (x = 0; x < length - 1; x += 2) {
bytes.push(parseInt(hex.substr(x, 2), 16));
}
return String.fromCharCode.apply(String, bytes);
}
// ---------------------------------------------------
/**
* SparkMD5 OOP implementation.
*
* Use this class to perform an incremental md5, otherwise use the
* static methods instead.
*/
function SparkMD5() {
// call reset to init the instance
this.reset();
}
/**
* Appends a string.
* A conversion will be applied if an utf8 string is detected.
*
* @param {String} str The string to be appended
*
* @return {SparkMD5} The instance itself
*/
SparkMD5.prototype.append = function (str) {
// Converts the string to utf8 bytes if necessary
// Then append as binary
this.appendBinary(toUtf8(str));
return this;
};
/**
* Appends a binary string.
*
* @param {String} contents The binary string to be appended
*
* @return {SparkMD5} The instance itself
*/
SparkMD5.prototype.appendBinary = function (contents) {
this._buff += contents;
this._length += contents.length;
var length = this._buff.length,
i;
for (i = 64; i <= length; i += 64) {
md5cycle(this._hash, md5blk(this._buff.substring(i - 64, i)));
}
this._buff = this._buff.substring(i - 64);
return this;
};
/**
* Finishes the incremental computation, reseting the internal state and
* returning the result.
*
* @param {Boolean} raw True to get the raw string, false to get the hex string
*
* @return {String} The result
*/
SparkMD5.prototype.end = function (raw) {
var buff = this._buff,
length = buff.length,
i,
tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
ret;
for (i = 0; i < length; i += 1) {
tail[i >> 2] |= buff.charCodeAt(i) << ((i % 4) << 3);
}
this._finish(tail, length);
ret = hex(this._hash);
if (raw) {
ret = hexToBinaryString(ret);
}
this.reset();
return ret;
};
/**
* Resets the internal state of the computation.
*
* @return {SparkMD5} The instance itself
*/
SparkMD5.prototype.reset = function () {
this._buff = '';
this._length = 0;
this._hash = [1732584193, -271733879, -1732584194, 271733878];
return this;
};
/**
* Gets the internal state of the computation.
*
* @return {Object} The state
*/
SparkMD5.prototype.getState = function () {
return {
buff: this._buff,
length: this._length,
hash: this._hash.slice()
};
};
/**
* Gets the internal state of the computation.
*
* @param {Object} state The state
*
* @return {SparkMD5} The instance itself
*/
SparkMD5.prototype.setState = function (state) {
this._buff = state.buff;
this._length = state.length;
this._hash = state.hash;
return this;
};
/**
* Releases memory used by the incremental buffer and other additional
* resources. If you plan to use the instance again, use reset instead.
*/
SparkMD5.prototype.destroy = function () {
delete this._hash;
delete this._buff;
delete this._length;
};
/**
* Finish the final calculation based on the tail.
*
* @param {Array} tail The tail (will be modified)
* @param {Number} length The length of the remaining buffer
*/
SparkMD5.prototype._finish = function (tail, length) {
var i = length,
tmp,
lo,
hi;
tail[i >> 2] |= 0x80 << ((i % 4) << 3);
if (i > 55) {
md5cycle(this._hash, tail);
for (i = 0; i < 16; i += 1) {
tail[i] = 0;
}
}
// Do the final computation based on the tail and length
// Beware that the final length may not fit in 32 bits so we take care of that
tmp = this._length * 8;
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
lo = parseInt(tmp[2], 16);
hi = parseInt(tmp[1], 16) || 0;
tail[14] = lo;
tail[15] = hi;
md5cycle(this._hash, tail);
};
/**
* Performs the md5 hash on a string.
* A conversion will be applied if utf8 string is detected.
*
* @param {String} str The string
* @param {Boolean} [raw] True to get the raw string, false to get the hex string
*
* @return {String} The result
*/
SparkMD5.hash = function (str, raw) {
// Converts the string to utf8 bytes if necessary
// Then compute it using the binary function
return SparkMD5.hashBinary(toUtf8(str), raw);
};
/**
* Performs the md5 hash on a binary string.
*
* @param {String} content The binary string
* @param {Boolean} [raw] True to get the raw string, false to get the hex string
*
* @return {String} The result
*/
SparkMD5.hashBinary = function (content, raw) {
var hash = md51(content),
ret = hex(hash);
return raw ? hexToBinaryString(ret) : ret;
};
// ---------------------------------------------------
/**
* SparkMD5 OOP implementation for array buffers.
*
* Use this class to perform an incremental md5 ONLY for array buffers.
*/
SparkMD5.ArrayBuffer = function () {
// call reset to init the instance
this.reset();
};
/**
* Appends an array buffer.
*
* @param {ArrayBuffer} arr The array to be appended
*
* @return {SparkMD5.ArrayBuffer} The instance itself
*/
SparkMD5.ArrayBuffer.prototype.append = function (arr) {
var buff = concatenateArrayBuffers(this._buff.buffer, arr, true),
length = buff.length,
i;
this._length += arr.byteLength;
for (i = 64; i <= length; i += 64) {
md5cycle(this._hash, md5blk_array(buff.subarray(i - 64, i)));
}
this._buff = (i - 64) < length ? new Uint8Array(buff.buffer.slice(i - 64)) : new Uint8Array(0);
return this;
};
/**
* Finishes the incremental computation, reseting the internal state and
* returning the result.
*
* @param {Boolean} raw True to get the raw string, false to get the hex string
*
* @return {String} The result
*/
SparkMD5.ArrayBuffer.prototype.end = function (raw) {
var buff = this._buff,
length = buff.length,
tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
i,
ret;
for (i = 0; i < length; i += 1) {
tail[i >> 2] |= buff[i] << ((i % 4) << 3);
}
this._finish(tail, length);
ret = hex(this._hash);
if (raw) {
ret = hexToBinaryString(ret);
}
this.reset();
return ret;
};
/**
* Resets the internal state of the computation.
*
* @return {SparkMD5.ArrayBuffer} The instance itself
*/
SparkMD5.ArrayBuffer.prototype.reset = function () {
this._buff = new Uint8Array(0);
this._length = 0;
this._hash = [1732584193, -271733879, -1732584194, 271733878];
return this;
};
/**
* Gets the internal state of the computation.
*
* @return {Object} The state
*/
SparkMD5.ArrayBuffer.prototype.getState = function () {
var state = SparkMD5.prototype.getState.call(this);
// Convert buffer to a string
state.buff = arrayBuffer2Utf8Str(state.buff);
return state;
};
/**
* Gets the internal state of the computation.
*
* @param {Object} state The state
*
* @return {SparkMD5.ArrayBuffer} The instance itself
*/
SparkMD5.ArrayBuffer.prototype.setState = function (state) {
// Convert string to buffer
state.buff = utf8Str2ArrayBuffer(state.buff, true);
return SparkMD5.prototype.setState.call(this, state);
};
SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy;
SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish;
/**
* Performs the md5 hash on an array buffer.
*
* @param {ArrayBuffer} arr The array buffer
* @param {Boolean} [raw] True to get the raw string, false to get the hex one
*
* @return {String} The result
*/
SparkMD5.ArrayBuffer.hash = function (arr, raw) {
var hash = md51_array(new Uint8Array(arr)),
ret = hex(hash);
return raw ? hexToBinaryString(ret) : ret;
};
return SparkMD5;
}));

1459
common/uni.css Normal file

File diff suppressed because it is too large Load Diff

6
common/vue-i18n.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,269 @@
<template>
<!-- 弹窗容器 -->
<view class="visitor-modal" v-if="visible">
<!-- 遮罩层点击关闭 -->
<view class="modal-mask" @click="closeModal"></view>
<!-- 弹窗内容阻止遮罩层事件穿透 -->
<view class="modal-dialog" @click.stop>
<!-- 头部标题 + 关闭按钮 -->
<view class="dialog-header">
<text class="dialog-title">访客详情</text>
<text class="dialog-close" @click="closeModal">×</text>
</view>
<!-- 主体信息展示 -->
<view class="dialog-body">
<view class="info-item">
<text class="item-label">访客姓名</text>
<text class="item-value">{{ visitorInfo.visitorName || '无' }}</text>
</view>
<view class="info-item">
<text class="item-label">访客电话</text>
<text class="item-value">{{ visitorInfo.visitorPhone || '无' }}</text>
</view>
<view class="info-item">
<text class="item-label">所属公司</text>
<text class="item-value">{{ visitorInfo.visitorUnit || '无' }}</text>
</view>
<view class="info-item">
<text class="item-label">被访人</text>
<text class="item-value">{{ visitorInfo.interviewedPerson || '无' }}</text>
</view>
<view class="info-item">
<text class="item-label">联系电话</text>
<text class="item-value">{{ visitorInfo.interviewedPhone || '无' }}</text>
</view>
<view class="info-item">
<text class="item-label">被访单位</text>
<text class="item-value">{{ visitorInfo.interviewedUnit || '无' }}</text>
</view>
<view class="info-item">
<text class="item-label">拜访事由</text>
<text class="item-value">{{ visitorInfo.visitingReason || '无' }}</text>
</view>
<view class="info-item">
<text class="item-label">拜访时间</text>
<text class="item-value">{{ visitorInfo.visitingBeginTime || '无' }}</text>
</view>
<view class="info-item">
<text class="item-label">是否预约车位</text>
<text class="item-value">{{ visitorInfo.bookingParkingSpace ? '是' : '否' }}</text>
</view>
<view class="info-item">
<text class="item-label">预约状态</text>
<text
class="item-value status-tag"
:class="{
'status-confirmed': visitorInfo.serveStatus === 1,
'status-pending': visitorInfo.serveStatus === 0,
'status-rejected': visitorInfo.serveStatus === 2
}"
>
{{ formatServeStatus(visitorInfo.serveStatus) }}
</text>
</view>
<view class="info-item">
<text class="item-label">提交时间</text>
<text class="item-value">{{ visitorInfo.createTime || '无' }}</text>
</view>
</view>
<!-- 底部审核操作仅待确认时显示 -->
<view class="dialog-footer" v-if="showActionButtons">
<button
class="audit-btn reject"
@click="handleAudit('reject')"
:loading="loading"
>
{{ loading && auditType === 'reject' ? '驳回中...' : '驳回' }}
</button>
<button
class="audit-btn confirm"
@click="handleAudit('confirm')"
:loading="loading"
>
{{ loading && auditType === 'confirm' ? '确认中...' : '确认' }}
</button>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
// 是否显示弹窗
visible: {
type: Boolean,
default: false
},
// 访客数据
visitorInfo: {
type: Object,
default: () => ({})
},
// 是否显示审核按钮(默认仅“待确认”显示)
showActionButtons: {
type: Boolean,
default: false
}
},
data() {
return {
loading: false, // 审核按钮加载状态
auditType: '' // 记录当前审核类型(确认/驳回)
}
},
methods: {
// 关闭弹窗
closeModal() {
this.$emit('close');
},
// 处理审核操作
handleAudit(type) {
this.loading = true;
this.auditType = type;
// 模拟接口请求(实际替换为真实接口)
setTimeout(() => {
this.loading = false;
this.closeModal(); // 关闭弹窗
// 通知父组件更新状态
this.$emit('audit', {
type: type,
visitorId: this.visitorInfo.id // 传递访客ID
});
// 提示用户
uni.showToast({
title: type === 'confirm' ? '确认成功' : '驳回成功',
icon: 'success'
});
}, 1500);
},
// 格式化服务状态
formatServeStatus(status) {
switch (status) {
case 0: return '待确认';
case 1: return '已确认';
case 2: return '已驳回';
default: return '未知';
}
}
}
}
</script>
<style scoped>
/* 弹窗容器 */
.visitor-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: flex-end;
align-items: flex-end;
z-index: 999;
}
/* 遮罩层 */
.modal-mask {
position: absolute;
width: 100%;
height: 100%;
}
/* 弹窗内容 */
.modal-dialog {
width: 100%;
background: #fff;
border-radius: 16px 16px 0 0;
overflow: hidden;
transform: translateY(100%);
animation: slideUp 0.3s forwards;
}
/* 弹窗滑入动画 */
@keyframes slideUp {
to {
transform: translateY(0);
}
}
/* 头部 */
.dialog-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px;
background: #f8f8f8;
border-bottom: 1px solid #eee;
}
.dialog-title {
font-size: 17px;
font-weight: bold;
}
.dialog-close {
font-size: 24px;
color: #999;
}
/* 主体内容 */
.dialog-body {
padding: 16px;
}
.info-item {
display: flex;
align-items: center;
margin-bottom: 12px;
}
.item-label {
width: 80px;
color: #666;
}
.item-value {
flex: 1;
color: #333;
}
.status-tag {
padding: 2px 8px;
border-radius: 4px;
color: #fff;
}
.status-pending {
background: #ff9900;
}
.status-confirmed {
background: #52c41a;
}
.status-rejected {
background: #ff4d4f;
}
/* 底部操作 */
.dialog-footer {
display: flex;
padding: 16px;
border-top: 1px solid #eee;
}
.audit-btn {
flex: 1;
margin: 0 8px;
padding: 12px 0;
border-radius: 8px;
color: #fff;
}
.reject {
background: #ff4d4f;
}
.confirm {
background: #52c41a;
}
</style>

View File

@@ -0,0 +1,64 @@
<template>
<view class="wrap wrap-home">
<u-navbar :title="title" height="44" :is-back="false">
<!-- #ifdef APP-PLUS --><!-- #endif -->
<view slot="right">
<view style="color: #22262d;font-size: 20px;" class="iconfont icon-setting-two" @click="navTo('/pages/sys/user/setting')"></view>
</view>
<u-avatar class="home-head" :src="avatarUrl" @click="show = true"></u-avatar>
</u-navbar>
</view>
</template>
<script>
export default {
props: {
title: {
type: String,
required: true
}
},
data() {
return {
src: '',
show: false,
};
},
onLoad() {
},
computed: {
avatarUrl() {
var url = this.vuex_config.baseUrl + this.vuex_user.avatar || '/static/aidex/tabbar/my_2.png';
url = this.replaceAll(url,'\\','/');
return url + '?t=' + new Date().getTime();
}
},
methods: {
showPersonalInfo() {
this.show = true
},
navTo(url) {
uni.navigateTo({
url: url
});
}
}
};
</script>
<style lang="scss">
.slot-wrap {
display: flex;
align-items: center;
/* 如果您想让slot内容占满整个导航栏的宽度 */
/* flex: 1; */
/* 如果您想让slot内容与导航栏左右有空隙 */
/* padding: 0 30rpx; */
}
page {
background-color: #ffffff;
}
</style>

Binary file not shown.

50
h5.html Normal file
View File

@@ -0,0 +1,50 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="keywords" content="PoweredByAidex"/>
<meta name="description" content="PoweredByAidex"/>
<link rel="shortcut icon" type="image/x-icon" href="<%= BASE_URL %>static/aidex/favicon.png">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<title><%= htmlWebpackPlugin.options.title %></title>
<script>
window.onresize = function () {
if (document.documentElement.clientWidth >= 768) {
window.location.href = '<%= BASE_URL %>static/#/';
}
};
window.onresize();
</script>
<!-- 正式发布的时候使用,开发期间不启用。↓ -->
<script src="<%= BASE_URL %>static/common/js/touch-emulator.js"></script>
<script>
TouchEmulator();
</script>
<style>
::-webkit-scrollbar{
display: none;
}
</style>
<!-- 正式发布的时候使用,开发期间不启用。↑ -->
<script>
document.addEventListener('DOMContentLoaded', function() {
document.documentElement.style.fontSize = document.documentElement.clientWidth / 20 + 'px'
})
</script>
<link rel="stylesheet" href="<%= BASE_URL %>static/index.css" />
</head>
<body>
<!-- 该文件为 H5 平台的模板 HTML并非应用入口。 -->
<!-- 请勿在此文件编写页面代码或直接运行此文件。 -->
<!-- 详见文档https://uniapp.dcloud.io/collocation/manifest?id=h5-template -->
<noscript>
<strong>本站点必须要开启JavaScript才能运行</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
<script>
/*BAIDU_STAT*/
</script>
</body>
</html>

64
main.js Normal file
View File

@@ -0,0 +1,64 @@
/**
* Copyright (c) 2013-Now http://aidex.vip All rights reserved.
*/
import Vue from 'vue';
import App from './App';
Vue.config.productionTip = false;
App.mpType = 'app';
// 引入全局 uView 框架
import uView from 'uview-ui';
Vue.use(uView);
// 全局存储 vuex 的封装
import store from '@/store';
// 引入 uView 提供的对 vuex 的简写法文件
let vuexStore = require('@/store/$u.mixin.js');
Vue.mixin(vuexStore);
// 引入 uView 对小程序分享的 mixin 封装
let mpShare = require('uview-ui/libs/mixin/mpShare.js');
Vue.mixin(mpShare);
// Vue i18n 国际化
import VueI18n from '@/common/vue-i18n.min.js';
Vue.use(VueI18n);
// i18n 部分的配置,引入语言包,注意路径
import lang_zh_CN from '@/common/locales/zh_CN.js';
import lang_en from '@/common/locales/en.js';
const i18n = new VueI18n({
// 默认语言
locale: 'zh_CN',
// 引入语言文件
messages: {
'zh_CN': lang_zh_CN,
'en': lang_en,
}
});
// 由于微信小程序的运行机制问题需声明如下一行H5和APP非必填
Vue.prototype._i18n = i18n;
const app = new Vue({
i18n,
store,
...App
});
import { replaceAll } from '@/common/common.js'
Vue.prototype.replaceAll = replaceAll
// http 拦截器,将此部分放在 new Vue() 和 app.$mount() 之间,才能 App.vue 中正常使用
import httpInterceptor from '@/common/http.interceptor.js';
Vue.use(httpInterceptor, app);
// http 接口 API 抽离,免于写 url 或者一些固定的参数
import httpApi from '@/common/http.api.js';
Vue.use(httpApi, app);
app.$mount();

38
manifest.json Normal file
View File

@@ -0,0 +1,38 @@
{
"name" : "访客机H5",
"appid" : "__UNI__BB8BA25",
"description" : "访客机H5",
"versionName" : "1.8.4",
"versionCode" : "100",
"transformPx" : false,
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compatible" : {
"ignoreVersion" : true
}
},
"mp-weixin" : {
"appid" : "wx04dd806ca2843cc1",
"setting" : {
"urlCheck" : true,
"es6" : false,
"minified" : true
},
"usingComponents" : true
},
"h5" : {
"title" : "访客机H5",
"devServer" : {
"https" : false
},
"router" : {
"mode" : "history",
"base" : "/visitore/"
}
},
"permissions" : {
"CAMERA" : {},
"WRITE_EXTERNAL_STORAGE" : {}
}
}

32
package-lock.json generated Normal file
View File

@@ -0,0 +1,32 @@
{
"name": "Aidex",
"version": "2.3.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@icon-park/vue": {
"version": "1.3.5",
"resolved": "https://registry.nlark.com/@icon-park/vue/download/@icon-park/vue-1.3.5.tgz",
"integrity": "sha1-twRuLCtdpexNZz/7Wta8gL6loTs=",
"requires": {
"@vue/babel-helper-vue-jsx-merge-props": "^1.0.0",
"csstype": "^3.0.3"
}
},
"@vue/babel-helper-vue-jsx-merge-props": {
"version": "1.2.1",
"resolved": "https://registry.npm.taobao.org/@vue/babel-helper-vue-jsx-merge-props/download/@vue/babel-helper-vue-jsx-merge-props-1.2.1.tgz",
"integrity": "sha1-MWJKelBfsU2h1YAjclpMXycOaoE="
},
"csstype": {
"version": "3.0.9",
"resolved": "https://registry.nlark.com/csstype/download/csstype-3.0.9.tgz?cache=0&sync_timestamp=1631540658518&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fcsstype%2Fdownload%2Fcsstype-3.0.9.tgz",
"integrity": "sha1-ZBCvMbJr0FIJM9AsvGT86c4/vws="
},
"vue-i18n": {
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-8.20.0.tgz",
"integrity": "sha512-ZiAOoeR4d/JtKpbjipx3I80ey7cYG1ki5gQ7HwzWm4YFio9brA15BEYHjalEoBaEfzF5OBEZP+Y2MvAaWnyXXg=="
}
}
}

27
package.json Normal file
View File

@@ -0,0 +1,27 @@
{
"name": "Aidex",
"version": "2.3.0",
"description": "Aidex 移动端快速开发框架",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://gitee.com/aidex/aidex-uniapp.git"
},
"keywords": [
"Aidex",
"快速开发平台"
],
"author": "aidex",
"license": "MIT",
"bugs": {
"url": "https://github.com/aidex/aidex-uniapp/issues"
},
"homepage": "https://github.com/aidex/aidex-uniapp#readme",
"dependencies": {
"@icon-park/vue": "^1.3.5",
"vue-i18n": "^8.20.0"
}
}

63
pages.json Normal file
View File

@@ -0,0 +1,63 @@
{
"easycom": {
"^u-(.*)": "@/uview-ui/components/u-$1/u-$1.vue"
},
"pages": [
{
"path": "pages/sys/msg/index",
"style": {
"navigationBarTitleText": "消息",
"navigationStyle": "custom" // 隐藏系统导航栏
}
}
],
"subPackages": [
],
"preloadRule": {
},
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "Aidex",
"navigationBarBackgroundColor": "#ffffff"
},
"tabBar": {
"color": "#333333",
"selectedColor": "#4094ff",
"backgroundColor": "#ffffff",
"borderStyle": "white",
"list": [
{
"pagePath": "pages/sys/msg/index",
"iconPath": "static/aidex/tabbar/msg_1.png",
"selectedIconPath": "static/aidex/tabbar/msg_2.png",
"text": "访客等级"
},
// {
// "pagePath": "pages/sys/home/index",
// "iconPath": "static/aidex/tabbar/home_1.png",
// "selectedIconPath": "static/aidex/tabbar/home_2.png",
// "text": "首页"
// },
{
"pagePath": "pages/sys/work/work",
"iconPath": "static/aidex/tabbar/apply_1.png",
"selectedIconPath": "static/aidex/tabbar/apply_2.png",
"text": "到访确认"
},
{
"pagePath": "pages/sys/qrpage/qrpage",
"iconPath": "static/aidex/tabbar/book_1.png",
"selectedIconPath": "static/aidex/tabbar/book_2.png",
"text": "扫码登记"
},
{
"pagePath": "pages/sys/camera/camera",
"iconPath": "static/aidex/tabbar/my_1.png",
"selectedIconPath": "static/aidex/tabbar/my_2.png",
"text": "我的"
}
]
}
}

108
pages/common/aidex.scss Normal file
View File

@@ -0,0 +1,108 @@
/*!
* Copyright (c) 2013-Now http://aidex.vip All rights reserved.
* @author aidex
* @version 2020-9-1
*/
.wrap {
.search{
padding: 20rpx 20rpx 0;
background: #f8f8f8;
height: 105rpx;
}
.scroll-list {
height: calc(100vh - var(--window-top) - var(--window-bottom) - 105rpx); // 105rpx 为 .search 的高度
width: 100%;
.loadmore {
padding: 30rpx;
}
}
.box {
padding-bottom: 10rpx;
.item {
margin: 0 20rpx 20rpx;
padding: 8rpx 20rpx;
border-radius: 20rpx;
box-sizing: border-box;
background-color: #fff;
font-size: 28rpx;
.title {
display: flex;
justify-content: space-between;
background-color: #fff;
padding-left: 15rpx;
align-items: center;
.text {
margin: 0 20rpx;
font-size: 35rpx;
font-weight: bold;
}
}
}
}
.list {
.u-cell-item-box {
.u-swipe-content {
width: 750rpx;
}
.u-cell_title {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
min-width: 655rpx;
}
.u-border-bottom:last-child:after {
border-bottom: 0;
}
}
}
.form {
display: flex;
flex-direction: column;
padding: 20rpx 30rpx;
.u-cell-item-box {
border-radius: 20rpx;
}
}
.form-footer {
display: flex;
margin: 10rpx;
padding-bottom: 30rpx;
.btn {
flex: 1;
margin: 20rpx;
}
}
.u-cell-box {
.u-cell {
font-size: 30rpx;
}
.u-cell_title {
font-size: 30rpx;
}
.u-cell__left-icon-wrap {
margin-right: 18rpx;
}
}
}

34
pages/common/webview.vue Normal file
View File

@@ -0,0 +1,34 @@
<template>
<view>
<web-view :webview-styles="webviewStyles" :src="webviewUrl"></web-view>
</view>
</template>
<script>
/**
* Copyright (c) 2013-Now http://aidex.vip All rights reserved.
*/
export default {
data() {
return {
webviewUrl: '',
webviewStyles: {
progress: {
color: '#FF7200'
}
}
};
},
onLoad(params) {
this.webviewUrl = params.url;
// this.webviewUrl = `http://127.0.0.1:8980/js/a/bpm/bpmCategory/index/process?__sid=${this.vuex_token}&__cookie=true`;
if (params.title != ''){
uni.setNavigationBarTitle({
title: params.title
})
}
}
};
</script>
<style lang="scss">
</style>

638
pages/sys/msg/index.vue Normal file
View File

@@ -0,0 +1,638 @@
<template>
<scroll-view class="container" scroll-y>
<view class="form-card">
<view class="form-title">访客预约表单</view>
<!-- 基本信息 -->
<view class="section-title">基本信息</view>
<view class="form-item">
<text class="label required">访客姓名</text>
<view class="input-wrapper">
<input type="text" placeholder="请输入您的姓名" v-model="formData.visitorName" />
</view>
</view>
<view class="form-item">
<text class="label required">所属公司</text>
<view class="input-wrapper">
<input type="text" placeholder="请输入您的公司名称" v-model="formData.visitorUnit" />
</view>
</view>
<view class="form-item">
<text class="label required">联系电话</text>
<view class="input-wrapper">
<input type="number" placeholder="请输入您的手机号码" v-model="formData.visitorPhone" />
</view>
</view>
<view class="form-item">
<text class="label required">拜访事由</text>
<view class="input-wrapper">
<input type="text" placeholder="请简要描述拜访目的" v-model="formData.visitingReason" />
</view>
</view>
<!-- 被访人信息 -->
<view class="section-title">被访人信息</view>
<view class="form-item">
<text class="label required">被访人姓名</text>
<view class="input-wrapper">
<input type="text" placeholder="请输入被访人姓名" v-model="formData.interviewedPerson" />
</view>
</view>
<view class="form-item">
<text class="label">被访单位</text>
<view class="input-wrapper">
<input type="text" placeholder="请输入被访单位名称" v-model="formData.interviewedUnit" />
</view>
</view>
<view class="form-item">
<text class="label">被访人电话</text>
<view class="input-wrapper">
<input type="number" placeholder="请输入被访人联系电话" v-model="formData.interviewedPhone" />
</view>
</view>
<!-- 拜访时间 -->
<view class="section-title">拜访时间</view>
<view class="form-item">
<text class="label">开始时间</text>
<view class="picker-wrapper">
<picker mode="date" :value="formData.visitingBeginDate" start="2020-01-01" end="2030-12-31"
@change="onBeginDateChange">
<view class="picker-value">{{formData.visitingBeginDate}}</view>
</picker>
</view>
<view class="picker-wrapper time-picker">
<picker mode="time" :value="formData.visitingBeginTime" @change="onBeginTimeChange">
<view class="picker-value">{{formData.visitingBeginTime}}</view>
</picker>
</view>
</view>
<view class="form-item">
<text class="label">结束时间</text>
<view class="picker-wrapper">
<picker mode="date" :value="formData.visitingEndDate" start="2020-01-01" end="2030-12-31"
@change="onEndDateChange">
<view class="picker-value">{{formData.visitingEndDate}}</view>
</picker>
</view>
<view class="picker-wrapper time-picker">
<picker mode="time" :value="formData.visitingEndTime" @change="onEndTimeChange">
<view class="picker-value">{{formData.visitingEndTime}}</view>
</picker>
</view>
</view>
<!-- 预约信息 -->
<view class="section-title">预约信息</view>
<view class="form-item">
<text class="label">预约车位</text>
<view class="picker-wrapper">
<picker mode="selector" :range="parkingOptions" @change="handleParkingChange">
<view class="picker-value">{{formData.bookingParkingSpace ? '需要' : '不需要'}}</view>
</picker>
</view>
</view>
<!-- 条件显示预约车位时显示车牌号 -->
<view class="form-item" v-if="formData.bookingParkingSpace">
<text class="label required">车牌号</text>
<view class="input-wrapper">
<input type="text" placeholder="请输入车牌号" v-model="formData.licensePlate" />
</view>
</view>
<view class="form-item">
<text class="label required">人脸照片</text>
<view class="upload-wrapper">
<view class="upload-btn" @click="takePhoto">
<text class="upload-text">+ 拍照上传</text>
</view>
<view class="preview-image" v-if="formData.facePictures">
<image :src="formData.facePictures" mode="aspectFill" @click="delimg"></image>
</view>
</view>
</view>
<!-- <view class="form-item">
<text class="label">预约状态</text>
<view class="picker-wrapper">
<picker mode="selector" :range="statusOptions" @change="handleStatusChange">
<view class="picker-value">{{statusMap[formData.serveStatus]}}</view>
</picker>
</view>
</view> -->
<!-- 提交按钮 -->
<view class="submit-wrapper">
<button class="submit-btn" @click="submitForm">确认提交</button>
</view>
</view>
</scroll-view>
</template>
<script>
export default {
data() {
const now = new Date();
const date = now.getFullYear() + '-' +
(now.getMonth() + 1).toString().padStart(2, '0') + '-' +
now.getDate().toString().padStart(2, '0');
const time = now.getHours().toString().padStart(2, '0') + ':' +
now.getMinutes().toString().padStart(2, '0');
return {
formData: {
visitorName: '1',
visitorUnit: '1',
visitorPhone: '15555555555',
visitingReason: '1',
interviewedPerson: '1',
interviewedUnit: '1',
interviewedPhone: '15555555555',
visitingBeginDate: date,
visitingBeginTime: time,
visitingEndDate: date,
visitingEndTime: time,
bookingParkingSpace: false,
licensePlate: '',
facePictures: '',
serveStatus: 0
},
parkingOptions: ['不需要', '需要'],
statusOptions: ['待确认', '已确认', '已取消', '已完成'],
statusMap: {
0: '待确认',
1: '已确认',
2: '已取消',
3: '已完成'
}
}
},
onLoad(options) {
// 新增获取url中的code参数并打印
if (options.code) {
console.log('从URL中获取到的code参数', options.code);
let p = {};
p.qrcode = options.code
this.formData.qrCodeId = options.code;
this.$u.api.codesub(p).then(res => {
console.log(res)
})
} else {
console.log('URL中未包含code参数');
uni.showToast({
title: '二维码失效,请重新扫描',
icon: 'success',
duration: 2000
});
}
// #ifdef APP-PLUS
plus.screen.lockOrientation('default');
// #endif
},
onReady() {
// #ifdef APP-PLUS
plus.screen.lockOrientation('landscape-primary');
// #endif
},
methods: {
// 处理日期时间选择
onBeginDateChange(e) {
this.formData.visitingBeginDate = e.detail.value;
},
onBeginTimeChange(e) {
this.formData.visitingBeginTime = e.detail.value;
},
onEndDateChange(e) {
this.formData.visitingEndDate = e.detail.value;
},
onEndTimeChange(e) {
this.formData.visitingEndTime = e.detail.value;
},
// 处理车位预约选择
handleParkingChange(e) {
this.formData.bookingParkingSpace = e.detail.value === 1;
if (!this.formData.bookingParkingSpace) {
this.formData.licensePlate = '';
}
},
// 处理状态选择
handleStatusChange(e) {
this.formData.serveStatus = e.detail.value;
},
// 拍照
takePhoto() {
uni.chooseImage({
count: 1,
sourceType: ['camera'], // 只允许使用相机
success: (res) => {
// console.log(res.tempFilePaths[0])
this.formData.facePictures = res.tempFilePaths[0];
},
fail: (err) => {
uni.showToast({
title: '拍照失败',
icon: 'none'
});
}
});
},
// 表单验证
validateForm() {
const {
qrCodeId,
visitorName,
visitorUnit,
visitorPhone,
visitingReason,
interviewedPerson,
bookingParkingSpace,
licensePlate,
facePictures,
visitingBeginDate,
visitingBeginTime,
visitingEndDate,
visitingEndTime
} = this.formData;
// 验证姓名
if (!visitorName) {
return '请输入访客姓名';
}
// 验证公司
if (!visitorUnit) {
return '请输入所属公司';
}
// 验证电话
if (!visitorPhone) {
return '请输入联系电话';
}
// 简单的手机号格式验证
if (!/^1[3-9]\d{9}$/.test(visitorPhone)) {
return '请输入正确的手机号码';
}
// 验证事由
if (!visitingReason) {
return '请输入拜访事由';
}
// 验证被访人
if (!interviewedPerson) {
return '请输入被访人姓名';
}
// 验证车牌号(如果需要预约车位)
if (bookingParkingSpace && !licensePlate) {
return '请输入车牌号';
}
if (!facePictures) {
return '请拍照上传人脸照片';
}
// 验证时间逻辑
const beginDateTime = new Date(`${visitingBeginDate} ${visitingBeginTime}`);
const endDateTime = new Date(`${visitingEndDate} ${visitingEndTime}`);
if (beginDateTime >= endDateTime) {
return '开始时间必须早于结束时间';
}
return ''; // 验证通过
},
// 提交表单
submitForm() {
const errorMsg = this.validateForm();
if (errorMsg) {
uni.showToast({
title: errorMsg,
icon: 'none'
});
return;
}
// 合并日期和时间
this.formData.visitingBeginTime = `${this.formData.visitingBeginDate} ${this.formData.visitingBeginTime}`;
this.formData.visitingEndTime = `${this.formData.visitingEndDate} ${this.formData.visitingEndTime}`;
// 准备提交数据
const submitData = {
...this.formData,
bookingParkingSpace: this.formData.bookingParkingSpace ? 0 : 1
};
console.log(submitData)
// 显示加载提示
uni.showLoading({
title: '提交中...',
mask: true
});
// 模拟API请求
setTimeout(() => {
uni.request({url:this.formData.facePictures})
// .then(response => response.blob())
.then(blob => {
// 此时得到了原始Blob对象
console.log(blob);
const file = new File([blob], 'filename.png', {
type: blob.type
});
// 构建FormData进行上传
const formData = new FormData();
formData.append('file', file);
console.log(formData)
// 发送上传请求
// this.$u.api.uploadimg()
// 接上面的代码假设Blob是图片类型
uni.uploadFile({
url: 'http://183.230.235.66:11010/api/resource/oss/qrupload', // 后端上传接口地址
filePath: file.filePath, // 要上传的文件路径
name: 'file', // 后端接收文件的参数名
// FormData中的其他参数
formData: {
'code': this.formData.qrCodeId // 示例:其他表单字段
},
// 上传进度回调
onProgressUpdate: (res) => {
this.progress = res.progress;
console.log('上传进度:' + res.progress);
},
// 上传成功回调
success: (res) => {
console.log('上传成功', res);
this.uploadResult = res.data;
uni.showToast({
title: '上传成功',
icon: 'success'
});
},
// 上传失败回调
fail: (err) => {
console.error('上传失败', err);
uni.showToast({
title: '上传失败',
icon: 'none'
});
},
// 无论成功失败都会执行
complete: () => {
this.progress = 0; // 重置进度
}
});
// this.$u.api.uploadimg(formData).then(res => {
// console.log(res)
// if (res.code == 200) {
// uni.showToast({
// title: "提交成功,请等待审核!",
// icon: "success"
// })
// } else {
// uni.showToast({
// title: "tp提交失败",
// icon: "error"
// })
// }
// })
});
this.$u.api.fksub(submitData).then(res => {
console.log(res)
if (res.code == 200) {
uni.showToast({
title: "提交成功,请等待审核!",
icon: "success"
})
} else {
uni.showToast({
title: "提交失败!",
icon: "error"
})
}
})
uni.hideLoading();
// 显示成功提示
uni.showToast({
title: '提交成功',
icon: 'success',
duration: 2000
});
// 提交成功后,可跳转到成功页面或重置表单
setTimeout(() => {
// 重置表单
this.resetForm();
// 返回到上一页或跳转到其他页面
// uni.navigateBack();
}, 2000);
}, 1500);
},
delimg() {
this.formData.facePictures = ''
},
// 重置表单
resetForm() {
const now = new Date();
const date = now.getFullYear() + '-' +
(now.getMonth() + 1).toString().padStart(2, '0') + '-' +
now.getDate().toString().padStart(2, '0');
const time = now.getHours().toString().padStart(2, '0') + ':' +
now.getMinutes().toString().padStart(2, '0');
this.formData = {
visitorName: '',
visitorUnit: '',
visitorPhone: '',
visitingReason: '',
interviewedPerson: '',
interviewedUnit: '',
interviewedPhone: '',
visitingBeginDate: date,
visitingBeginTime: time,
visitingEndDate: date,
visitingEndTime: time,
bookingParkingSpace: false,
licensePlate: '',
facePictures: '',
serveStatus: 0
};
}
}
}
</script>
<style scoped>
.container {
background-color: #f5f7fa;
min-height: 100vh;
}
.form-card {
background-color: #fff;
border-radius: 12px;
margin: 16px;
padding: 20px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
}
.form-title {
font-size: 20px;
font-weight: bold;
color: #333;
text-align: center;
margin-bottom: 24px;
}
.section-title {
font-size: 16px;
font-weight: 600;
color: #333;
margin-top: 20px;
margin-bottom: 12px;
padding-left: 8px;
border-left: 3px solid #007aff;
}
.form-item {
display: flex;
align-items: center;
margin-bottom: 16px;
}
.label {
width: 96px;
font-size: 14px;
color: #666;
margin-right: 12px;
}
.label.required::before {
content: '*';
color: #ff4d4f;
margin-right: 4px;
}
.input-wrapper {
flex: 1;
height: 40px;
border: 1px solid #e5e6eb;
border-radius: 6px;
padding: 0 12px;
display: flex;
align-items: center;
}
.input-wrapper input {
flex: 1;
height: 100%;
font-size: 14px;
color: #333;
}
.picker-wrapper {
flex: 1;
height: 40px;
border: 1px solid #e5e6eb;
border-radius: 6px;
padding: 0 12px;
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
}
.picker-wrapper::after {
content: '';
width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 6px solid #999;
position: absolute;
right: 12px;
}
.time-picker {
margin-left: 10px;
}
.picker-value {
font-size: 14px;
color: #333;
flex: 1;
}
.upload-wrapper {
flex: 1;
display: flex;
align-items: center;
}
.upload-btn {
width: 80px;
height: 80px;
background-color: #f5f7fa;
border-radius: 6px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border: 1px dashed #e5e6eb;
}
.upload-text {
font-size: 12px;
color: #999;
margin-top: 4px;
}
.preview-image {
width: 80px;
height: 80px;
border-radius: 6px;
margin-left: 12px;
overflow: hidden;
border: 1px solid #e5e6eb;
}
.preview-image image {
width: 100%;
height: 100%;
}
.submit-wrapper {
margin-top: 32px;
}
.submit-btn {
width: 100%;
height: 44px;
background-color: #007aff;
color: #fff;
font-size: 16px;
font-weight: 500;
border-radius: 22px;
display: flex;
align-items: center;
justify-content: center;
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
static/aidex/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
static/aidex/login-bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@@ -0,0 +1,363 @@
(function(window, document, exportName, undefined) {
"use strict";
var isMultiTouch = false;
var multiTouchStartPos;
var eventTarget;
var touchElements = {};
// polyfills
if(!document.createTouch) {
document.createTouch = function(view, target, identifier, pageX, pageY, screenX, screenY, clientX, clientY) {
// auto set
if(clientX == undefined || clientY == undefined) {
clientX = pageX - window.pageXOffset;
clientY = pageY - window.pageYOffset;
}
return new Touch(target, identifier, {
pageX: pageX,
pageY: pageY,
screenX: screenX,
screenY: screenY,
clientX: clientX,
clientY: clientY
});
};
}
if(!document.createTouchList) {
document.createTouchList = function() {
var touchList = new TouchList();
for (var i = 0; i < arguments.length; i++) {
touchList[i] = arguments[i];
}
touchList.length = arguments.length;
return touchList;
};
}
/**
* create an touch point
* @constructor
* @param target
* @param identifier
* @param pos
* @param deltaX
* @param deltaY
* @returns {Object} touchPoint
*/
function Touch(target, identifier, pos, deltaX, deltaY) {
deltaX = deltaX || 0;
deltaY = deltaY || 0;
this.identifier = identifier;
this.target = target;
this.clientX = pos.clientX + deltaX;
this.clientY = pos.clientY + deltaY;
this.screenX = pos.screenX + deltaX;
this.screenY = pos.screenY + deltaY;
this.pageX = pos.pageX + deltaX;
this.pageY = pos.pageY + deltaY;
}
/**
* create empty touchlist with the methods
* @constructor
* @returns touchList
*/
function TouchList() {
var touchList = [];
touchList.item = function(index) {
return this[index] || null;
};
// specified by Mozilla
touchList.identifiedTouch = function(id) {
return this[id + 1] || null;
};
return touchList;
}
/**
* Simple trick to fake touch event support
* this is enough for most libraries like Modernizr and Hammer
*/
function fakeTouchSupport() {
var objs = [window, document.documentElement];
var props = ['ontouchstart', 'ontouchmove', 'ontouchcancel', 'ontouchend'];
for(var o=0; o<objs.length; o++) {
for(var p=0; p<props.length; p++) {
if(objs[o] && objs[o][props[p]] == undefined) {
objs[o][props[p]] = null;
}
}
}
}
/**
* we don't have to emulate on a touch device
* @returns {boolean}
*/
function hasTouchSupport() {
return ("ontouchstart" in window) || // touch events
(window.Modernizr && window.Modernizr.touch) || // modernizr
(navigator.msMaxTouchPoints || navigator.maxTouchPoints) > 2; // pointer events
}
/**
* disable mouseevents on the page
* @param ev
*/
function preventMouseEvents(ev) {
// 注释启用默认事件
// ev.preventDefault();
// ev.stopPropagation();
}
/**
* only trigger touches when the left mousebutton has been pressed
* @param touchType
* @returns {Function}
*/
function onMouse(touchType) {
return function(ev) {
// prevent mouse events
preventMouseEvents(ev);
if (ev.which !== 1) {
return;
}
// The EventTarget on which the touch point started when it was first placed on the surface,
// even if the touch point has since moved outside the interactive area of that element.
// also, when the target doesnt exist anymore, we update it
if (ev.type == 'mousedown' || !eventTarget || (eventTarget && !eventTarget.dispatchEvent)) {
eventTarget = ev.target;
}
// shiftKey has been lost, so trigger a touchend
if (isMultiTouch && !ev.shiftKey) {
triggerTouch('touchend', ev);
isMultiTouch = false;
}
triggerTouch(touchType, ev);
// we're entering the multi-touch mode!
if (!isMultiTouch && ev.shiftKey) {
isMultiTouch = true;
multiTouchStartPos = {
pageX: ev.pageX,
pageY: ev.pageY,
clientX: ev.clientX,
clientY: ev.clientY,
screenX: ev.screenX,
screenY: ev.screenY
};
triggerTouch('touchstart', ev);
}
// reset
if (ev.type == 'mouseup') {
multiTouchStartPos = null;
isMultiTouch = false;
eventTarget = null;
}
}
}
/**
* trigger a touch event
* @param eventName
* @param mouseEv
*/
function triggerTouch(eventName, mouseEv) {
var touchEvent = document.createEvent('Event');
touchEvent.initEvent(eventName, true, true);
touchEvent.altKey = mouseEv.altKey;
touchEvent.ctrlKey = mouseEv.ctrlKey;
touchEvent.metaKey = mouseEv.metaKey;
touchEvent.shiftKey = mouseEv.shiftKey;
touchEvent.touches = getActiveTouches(mouseEv, eventName);
touchEvent.targetTouches = getActiveTouches(mouseEv, eventName);
touchEvent.changedTouches = getChangedTouches(mouseEv, eventName);
eventTarget.dispatchEvent(touchEvent);
}
/**
* create a touchList based on the mouse event
* @param mouseEv
* @returns {TouchList}
*/
function createTouchList(mouseEv) {
var touchList = new TouchList();
if (isMultiTouch) {
var f = TouchEmulator.multiTouchOffset;
var deltaX = multiTouchStartPos.pageX - mouseEv.pageX;
var deltaY = multiTouchStartPos.pageY - mouseEv.pageY;
touchList.push(new Touch(eventTarget, 1, multiTouchStartPos, (deltaX*-1) - f, (deltaY*-1) + f));
touchList.push(new Touch(eventTarget, 2, multiTouchStartPos, deltaX+f, deltaY-f));
} else {
touchList.push(new Touch(eventTarget, 1, mouseEv, 0, 0));
}
return touchList;
}
/**
* receive all active touches
* @param mouseEv
* @returns {TouchList}
*/
function getActiveTouches(mouseEv, eventName) {
// empty list
if (mouseEv.type == 'mouseup') {
return new TouchList();
}
var touchList = createTouchList(mouseEv);
if(isMultiTouch && mouseEv.type != 'mouseup' && eventName == 'touchend') {
touchList.splice(1, 1);
}
return touchList;
}
/**
* receive a filtered set of touches with only the changed pointers
* @param mouseEv
* @param eventName
* @returns {TouchList}
*/
function getChangedTouches(mouseEv, eventName) {
var touchList = createTouchList(mouseEv);
// we only want to return the added/removed item on multitouch
// which is the second pointer, so remove the first pointer from the touchList
//
// but when the mouseEv.type is mouseup, we want to send all touches because then
// no new input will be possible
if(isMultiTouch && mouseEv.type != 'mouseup' &&
(eventName == 'touchstart' || eventName == 'touchend')) {
touchList.splice(0, 1);
}
return touchList;
}
/**
* show the touchpoints on the screen
*/
function showTouches(ev) {
var touch, i, el, styles;
// first all visible touches
for(i = 0; i < ev.touches.length; i++) {
touch = ev.touches[i];
el = touchElements[touch.identifier];
if(!el) {
el = touchElements[touch.identifier] = document.createElement("div");
document.body.appendChild(el);
}
styles = TouchEmulator.template(touch);
for(var prop in styles) {
el.style[prop] = styles[prop];
}
}
// remove all ended touches
if(ev.type == 'touchend' || ev.type == 'touchcancel') {
for(i = 0; i < ev.changedTouches.length; i++) {
touch = ev.changedTouches[i];
el = touchElements[touch.identifier];
if(el) {
el.parentNode.removeChild(el);
delete touchElements[touch.identifier];
}
}
}
}
/**
* TouchEmulator initializer
*/
function TouchEmulator() {
if (hasTouchSupport()) {
return;
}
fakeTouchSupport();
window.addEventListener("mousedown", onMouse('touchstart'), true);
window.addEventListener("mousemove", onMouse('touchmove'), true);
window.addEventListener("mouseup", onMouse('touchend'), true);
window.addEventListener("mouseenter", preventMouseEvents, true);
window.addEventListener("mouseleave", preventMouseEvents, true);
window.addEventListener("mouseout", preventMouseEvents, true);
window.addEventListener("mouseover", preventMouseEvents, true);
// it uses itself!
window.addEventListener("touchstart", showTouches, true);
window.addEventListener("touchmove", showTouches, true);
window.addEventListener("touchend", showTouches, true);
window.addEventListener("touchcancel", showTouches, true);
}
// start distance when entering the multitouch mode
TouchEmulator.multiTouchOffset = 75;
/**
* css template for the touch rendering
* @param touch
* @returns object
*/
TouchEmulator.template = function(touch) {
var size = 0;
var transform = 'translate('+ (touch.clientX-(size/2)) +'px, '+ (touch.clientY-(size/2)) +'px)';
return {
position: 'fixed',
left: 0,
top: 0,
background: '#fff',
border: 'solid 1px #999',
opacity: .6,
borderRadius: '100%',
height: size + 'px',
width: size + 'px',
padding: 0,
margin: 0,
display: 'block',
overflow: 'hidden',
pointerEvents: 'none',
webkitUserSelect: 'none',
mozUserSelect: 'none',
userSelect: 'none',
webkitTransform: transform,
mozTransform: transform,
transform: transform,
zIndex: 100
}
};
// export
if (typeof define == "function" && define.amd) {
define(function() {
return TouchEmulator;
});
} else if (typeof module != "undefined" && module.exports) {
module.exports = TouchEmulator;
} else {
window[exportName] = TouchEmulator;
}
})(window, document, "TouchEmulator");

View File

@@ -0,0 +1,463 @@
@font-face {
font-family: "iconfont"; /* Project id 2874232 */
src: url('~@/static/iconfont/iconfont.woff2?t=1636514770782') format('woff2'),
url('~@/static/iconfont/iconfont.woff?t=1636514770782') format('woff'),
url('~@/static/iconfont/iconfont.ttf?t=1636514770782') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-minus-circle-fill:before {
content: "\e844";
}
.icon-close-circle-fill:before {
content: "\e845";
}
.icon-plus-circle-fill:before {
content: "\e846";
}
.icon-tupian:before {
content: "\e8ba";
}
.icon-xiangji:before {
content: "\e8bc";
}
.icon-zengjia:before {
content: "\e8c0";
}
.icon-youhuiquan:before {
content: "\e8c1";
}
.icon-quanbudingdan:before {
content: "\e600";
}
.icon-moban:before {
content: "\e6bb";
}
.icon-hetongqianzi:before {
content: "\e615";
}
.icon-gongwujiedai:before {
content: "\e609";
}
.icon-kaoqinchuqin:before {
content: "\e8d0";
}
.icon-haocaifei:before {
content: "\e6bc";
}
.icon-huiyishi1:before {
content: "\e662";
}
.icon-baoming1:before {
content: "\e632";
}
.icon-jiabanshenqing:before {
content: "\e651";
}
.icon-hetongxieyi:before {
content: "\e64b";
}
.icon-jiabanshenpi:before {
content: "\e774";
}
.icon-yongche:before {
content: "\e601";
}
.icon-baoming:before {
content: "\e664";
}
.icon-qingjia:before {
content: "\e624";
}
.icon-tianshenpi:before {
content: "\eb67";
}
.icon-icon_yingyongguanli:before {
content: "\eb8f";
}
.icon-xingzhuang-xingxing:before {
content: "\eb9a";
}
.icon-gongnengdingyi:before {
content: "\ebb7";
}
.icon-kongxinduigou:before {
content: "\ebe5";
}
.icon-tianjia:before {
content: "\e620";
}
.icon-chucha:before {
content: "\e60f";
}
.icon-gongdan:before {
content: "\ec37";
}
.icon-daibanshixiang2:before {
content: "\ec4e";
}
.icon-bianjisekuai:before {
content: "\ec7c";
}
.icon-hetongguanli:before {
content: "\e625";
}
.icon-huiyishi:before {
content: "\e608";
}
.icon-ribao:before {
content: "\e835";
}
.icon-banjieshiwu:before {
content: "\e602";
}
.icon-daibanshiwu:before {
content: "\e603";
}
.icon-kaoheguanli:before {
content: "\e606";
}
.icon-shiyanshikaohe:before {
content: "\e607";
}
.icon-baoxiao:before {
content: "\e605";
}
.icon-shenpi:before {
content: "\e626";
}
.icon-baoxiaodan:before {
content: "\e61b";
}
.icon-xinwen:before {
content: "\e639";
}
.icon-tongzhi:before {
content: "\e648";
}
.icon-fujian:before {
content: "\e655";
}
.icon-msg-system:before {
content: "\e6b9";
}
.icon-daibanshixiang:before {
content: "\e65d";
}
.icon-tongzhi1:before {
content: "\e64a";
}
.icon-daibanshixiang1:before {
content: "\e6ba";
}
.icon-search:before {
content: "\e6b4";
}
.icon-view-list:before {
content: "\e6b5";
}
.icon-headset-one:before {
content: "\e6b6";
}
.icon-list-checkbox:before {
content: "\e6b7";
}
.icon-jiyika:before {
content: "\e6b8";
}
.icon-chart-histogram-two:before {
content: "\e679";
}
.icon-audit:before {
content: "\e67a";
}
.icon-check-one:before {
content: "\e67b";
}
.icon-bookmark-one:before {
content: "\e67c";
}
.icon-a-comment1:before {
content: "\e67d";
}
.icon-avatar:before {
content: "\e67e";
}
.icon-collection-files:before {
content: "\e67f";
}
.icon-copy-one:before {
content: "\e680";
}
.icon-add:before {
content: "\e681";
}
.icon-currency:before {
content: "\e682";
}
.icon-edit-two:before {
content: "\e683";
}
.icon-finance:before {
content: "\e684";
}
.icon-find:before {
content: "\e685";
}
.icon-folder-plus:before {
content: "\e686";
}
.icon-link-break:before {
content: "\e687";
}
.icon-financing-one:before {
content: "\e688";
}
.icon-help:before {
content: "\e689";
}
.icon-chart-pie:before {
content: "\e68a";
}
.icon-id-card:before {
content: "\e68b";
}
.icon-a-lock1:before {
content: "\e68c";
}
.icon-list:before {
content: "\e68d";
}
.icon-lock:before {
content: "\e68e";
}
.icon-key:before {
content: "\e68f";
}
.icon-a-key1:before {
content: "\e690";
}
.icon-me:before {
content: "\e691";
}
.icon-equalizer:before {
content: "\e692";
}
.icon-comment:before {
content: "\e693";
}
.icon-log:before {
content: "\e694";
}
.icon-mall-bag:before {
content: "\e695";
}
.icon-list-view:before {
content: "\e696";
}
.icon-send:before {
content: "\e697";
}
.icon-people:before {
content: "\e698";
}
.icon-peoples:before {
content: "\e699";
}
.icon-a-message-one1:before {
content: "\e69a";
}
.icon-phone-telephone:before {
content: "\e69b";
}
.icon-internal-transmission:before {
content: "\e69c";
}
.icon-schedule:before {
content: "\e69d";
}
.icon-more-one:before {
content: "\e69e";
}
.icon-sim:before {
content: "\e69f";
}
.icon-a-peoples1:before {
content: "\e6a0";
}
.icon-wallet:before {
content: "\e6a1";
}
.icon-permissions:before {
content: "\e6a2";
}
.icon-faan:before {
content: "\e6a3";
}
.icon-transporter:before {
content: "\e6a4";
}
.icon-transaction-order:before {
content: "\e6a5";
}
.icon-message-one:before {
content: "\e6a6";
}
.icon-shouji:before {
content: "\e6a7";
}
.icon-liebiaochakanmoshi_view-grid-list:before {
content: "\e6a8";
}
.icon-time:before {
content: "\e6a9";
}
.icon-transaction:before {
content: "\e6aa";
}
.icon-setting-two:before {
content: "\e6ab";
}
.icon-plan:before {
content: "\e6ac";
}
.icon-a-time1:before {
content: "\e6ad";
}
.icon-shezhi_setting:before {
content: "\e6ae";
}
.icon-zanting:before {
content: "\e6af";
}
.icon-sousuo_search:before {
content: "\e6b0";
}
.icon-xiangqingliebiao:before {
content: "\e6b1";
}
.icon-workbench:before {
content: "\e6b2";
}
.icon-shujubiao_data-sheet:before {
content: "\e6b3";
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

51
static/index.html Normal file
View File

@@ -0,0 +1,51 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="keywords" content="PoweredByAidex"/>
<link rel="shortcut icon" type="image/x-icon" href="static/aidex/favicon.png">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<title>aidex Mobile APP</title>
<script>
window.onresize = function () {
if (document.documentElement.clientWidth < 768) {
window.location.href = '../#/';
}
};
window.onresize();
</script>
<style>
.mobile-model {
margin: 10px auto;
background-color: #fff;
width: 330px;
margin-top: calc(50vh - 350px);
box-sizing: border-box;
background-image: url(common/img/iPhoneX.png);
background-repeat: no-repeat;
background-size: 100%;
border-radius: 30px;
padding: 48px 23px 38px 16px;
}
.mobile-content {
box-sizing: border-box;
width: 298px;
height: 582px;
border-bottom-left-radius: 20px;
}
.mobile-iframe {
height: 100%;
width: 100%;
border-radius: 20px;
}
</style>
</head>
<body>
<div class="mobile-model">
<div class="mobile-content">
<iframe src="../#/" class="mobile-iframe" scrolling="auto" frameborder="0"></iframe>
</div>
</div>
</body>
</html>

BIN
static/uni.ttf Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
static/uview/example/js.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

27
store/$u.mixin.js Normal file
View File

@@ -0,0 +1,27 @@
import { mapState } from 'vuex'
import store from "@/store"
// 尝试将用户在根目录中的store/index.js的vuex的state变量全部加载到全局变量中
let $uStoreKey = [];
try{
$uStoreKey = store.state ? Object.keys(store.state) : [];
}catch(e){
}
module.exports = {
beforeCreate() {
// 将vuex方法挂在到$u中
// 使用方法为如果要修改vuex的state中的user.name变量为"史诗" => this.$u.vuex('user.name', '史诗')
// 如果要修改vuex的state的version变量为1.0.1 => this.$u.vuex('version', '1.0.1')
this.$u.vuex = (name, value) => {
this.$store.commit('$uStore', {
name,value
})
}
},
computed: {
// 将vuex的state中的所有变量解构到全局混入的mixin中
...mapState($uStoreKey)
}
}

94
store/index.js Normal file
View File

@@ -0,0 +1,94 @@
/**
* Copyright (c) 2013-Now http://aidex.vip All rights reserved.
*/
import config from '@/common/config.js';
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
let lifeData = {};
try{
// 尝试获取本地是否存在lifeData变量第一次启动APP时是不存在的
lifeData = uni.getStorageSync('lifeData');
}catch(e){
}
// 需要永久存储且下次APP启动需要取出的在state中的变量名
let saveStateKeys = ['vuex_user', 'vuex_token', 'vuex_remember', 'vuex_locale','vuex_isAgent'];
// 保存变量到本地存储中
const saveLifeData = function(key, value){
// 判断变量名是否在需要存储的数组中
if(saveStateKeys.indexOf(key) != -1) {
// 获取本地存储的lifeData对象将变量添加到对象中
let tmp = uni.getStorageSync('lifeData');
// 第一次打开APP不存在lifeData变量故放一个{}空对象
tmp = tmp ? tmp : {};
tmp[key] = value;
// 执行这一步后所有需要存储的变量都挂载在本地的lifeData对象中
uni.setStorageSync('lifeData', tmp);
}
}
// 简化 vuex 操作文档https://uviewui.com/components/vuexDetail.html
const store = new Vuex.Store({
state: {
// 如果上面从本地获取的lifeData对象下有对应的属性就赋值给state中对应的变量
// 加上vuex_前缀是防止变量名冲突也让人一目了然
vuex_user: lifeData.vuex_user ? lifeData.vuex_user : {userName: 'Aidex'},
vuex_token: lifeData.vuex_token ? lifeData.vuex_token : '',
vuex_remember: lifeData.vuex_remember ? lifeData.vuex_remember : '',
vuex_locale: lifeData.vuex_locale ? lifeData.vuex_locale : '',
vuex_isAgent: lifeData.vuex_isAgent ? lifeData.vuex_isAgent : '',
// 如果vuex_version无需保存到本地永久存储无需lifeData.vuex_version方式
vuex_config: config,
// 自定义tabbar数据
// vuex_tabbar: [{
// iconPath: "/static/uview/example/component.png",
// selectedIconPath: "/static/uview/example/component_select.png",
// text: '组件',
// pagePath: '/pages/example/components'
// },
// {
// iconPath: "/static/uview/example/js.png",
// selectedIconPath: "/static/uview/example/js_select.png",
// text: '工具',
// midButton: true,
// pagePath: '/pages/example/js'
// },
// {
// iconPath: "/static/uview/example/template.png",
// selectedIconPath: "/static/uview/example/template_select.png",
// text: '模板',
// pagePath: '/pages/example/template'
// }
// ]
},
mutations: {
$uStore(state, payload) {
// 判断是否多层级调用state中为对象存在的情况诸如user.info.score = 1
let nameArr = payload.name.split('.');
let saveKey = '';
let len = nameArr.length;
if(len >= 2) {
let obj = state[nameArr[0]];
for(let i = 1; i < len - 1; i ++) {
obj = obj[nameArr[i]];
}
obj[nameArr[len - 1]] = payload.value;
saveKey = nameArr[0];
} else {
// 单层级变量在state就是一个普通变量的情况
state[payload.name] = payload.value;
saveKey = payload.name;
}
// 保存变量到本地,见顶部函数定义
saveLifeData(saveKey, state[saveKey])
}
}
})
export default store

41
uni.scss Normal file
View File

@@ -0,0 +1,41 @@
/**
* Copyright (c) 2013-Now http://aidex.vip All rights reserved.
* 下方引入的为uView UI的集成样式文件为scss预处理器其中包含了一些"u-"开头的自定义变量
* 使用的时候请将下面的一行复制到您的uniapp项目根目录的uni.scss中即可
* uView自定义的css类名和scss变量均以"u-"开头,不会造成冲突,请放心使用
*/
$u-main-color: #303133;
$u-content-color: #505256;
$u-tips-color: #909399;
$u-light-color: #c0c4cc;
$u-border-color: #dedfe2;
$u-bg-color: #f3f4f6;
$u-type-primary: #2979ff;
$u-type-primary-light: #ecf5ff;
$u-type-primary-disabled: #a0cfff;
$u-type-primary-dark: #2b85e4;
$u-type-warning: #ff9900;
$u-type-warning-disabled: #fcbd71;
$u-type-warning-dark: #f29100;
$u-type-warning-light: #fdf6ec;
$u-type-success: #19be6b;
$u-type-success-disabled: #71d5a1;
$u-type-success-dark: #18b566;
$u-type-success-light: #dbf1e1;
$u-type-error: #fa3534;
$u-type-error-disabled: #fab6b6;
$u-type-error-dark: #dd6161;
$u-type-error-light: #fef0f0;
$u-type-info: #909399;
$u-type-info-disabled: #c8c9cc;
$u-type-info-dark: #82848a;
$u-type-info-light: #f4f4f5;
$u-form-item-height: 70rpx;
$u-form-item-border-color: #dcdfe6;

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 705 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

21
uview-ui/LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 www.uviewui.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

106
uview-ui/README.md Normal file
View File

@@ -0,0 +1,106 @@
<p align="center">
<img alt="logo" src="https://uviewui.com/common/logo.png" width="120" height="120" style="margin-bottom: 10px;">
</p>
<h3 align="center" style="margin: 30px 0 30px;font-weight: bold;font-size:40px;">uView</h3>
<h3 align="center">多平台快速开发的UI框架</h3>
## 说明
uView UI是[uni-app](https://uniapp.dcloud.io/)生态优秀的UI框架全面的组件和便捷的工具会让您信手拈来如鱼得水
## 特性
- 兼容安卓iOS微信小程序H5QQ小程序百度小程序支付宝小程序头条小程序
- 60+精选组件,功能丰富,多端兼容,让您快速集成,开箱即用
- 众多贴心的JS利器让您飞镖在手召之即来百步穿杨
- 众多的常用页面和布局,让您专注逻辑,事半功倍
- 详尽的文档支持,现代化的演示效果
- 按需引入,精简打包体积
## 安装
```bash
# npm方式安装
npm i uview-ui
```
## 快速上手
1. `main.js`引入uView库
```js
// main.js
import uView from 'uview-ui';
Vue.use(uView);
```
2. `App.vue`引入基础样式(注意style标签需声明scss属性支持)
```css
/* App.vue */
<style lang="scss">
@import "uview-ui/index.scss";
</style>
```
3. `uni.scss`引入全局scss变量文件
```css
/* uni.scss */
@import "uview-ui/theme.scss";
```
4. `pages.json`配置easycom规则(按需引入)
```js
// pages.json
{
"easycom": {
// npm安装的方式不需要前面的"@/",下载安装的方式需要"@/"
// npm安装方式
"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
// 下载安装方式
// "^u-(.*)": "@/uview-ui/components/u-$1/u-$1.vue"
},
// 此为本身已有的内容
"pages": [
// ......
]
}
```
请通过[快速上手](https://uviewui.com/components/quickstart.html)了解更详细的内容
## 使用方法
配置easycom规则后自动按需引入无需`import`组件,直接引用即可。
```html
<template>
<u-button>按钮</u-button>
</template>
```
请通过[快速上手](https://uviewui.com/components/quickstart.html)了解更详细的内容
## 链接
- [官方文档](https://uviewui.com/)
- [更新日志](https://uviewui.com/components/changelog.html)
- [升级指南](https://uviewui.com/components/changelog.html)
- [关于我们](https://uviewui.com/cooperation/about.html)
## 预览
您可以通过**微信**扫码,查看最佳的演示效果。
<br>
<br>
<img src="https://uviewui.com/common/weixin_mini_qrcode.png" width="220" height="220" >
<!-- ## 捐赠uView的研发
uView文档和源码全部开源免费如果您认为uView帮到了您的开发工作您可以捐赠uView的研发工作捐赠无门槛哪怕是一杯可乐也好(相信这比打赏主播更有意义)。
<img src="https://uviewui.com/common/wechat.png" width="220" >
<img style="margin-left: 100px;" src="https://uviewui.com/common/alipay.png" width="220" >
-->
## 版权信息
uView遵循[MIT](https://en.wikipedia.org/wiki/MIT_License)开源协议意味着您无需支付任何费用也无需授权即可将uView应用到您的产品中。

View File

@@ -0,0 +1,190 @@
<template>
<u-popup mode="bottom" :border-radius="borderRadius" :popup="false" v-model="value" :maskCloseAble="maskCloseAble"
length="auto" :safeAreaInsetBottom="safeAreaInsetBottom" @close="popupClose" :z-index="uZIndex">
<view class="u-tips u-border-bottom" v-if="tips.text" :style="[tipsStyle]">
{{tips.text}}
</view>
<block v-for="(item, index) in list" :key="index">
<view
@touchmove.stop.prevent
@tap="itemClick(index)"
:style="[itemStyle(index)]"
class="u-action-sheet-item u-line-1"
:class="[index < list.length - 1 ? 'u-border-bottom' : '']"
:hover-stay-time="150"
>
<text>{{item.text}}</text>
<text class="u-action-sheet-item__subtext u-line-1" v-if="item.subText">{{item.subText}}</text>
</view>
</block>
<view class="u-gab" v-if="cancelBtn">
</view>
<view @touchmove.stop.prevent class="u-actionsheet-cancel u-action-sheet-item" hover-class="u-hover-class"
:hover-stay-time="150" v-if="cancelBtn" @tap="close">{{cancelText}}</view>
</u-popup>
</template>
<script>
/**
* actionSheet 操作菜单
* @description 本组件用于从底部弹出一个操作菜单供用户选择并返回结果。本组件功能类似于uni的uni.showActionSheetAPI配置更加灵活所有平台都表现一致。
* @tutorial https://www.uviewui.com/components/actionSheet.html
* @property {Array<Object>} list 按钮的文字数组,见官方文档示例
* @property {Object} tips 顶部的提示文字,见官方文档示例
* @property {String} cancel-text 取消按钮的提示文字
* @property {Boolean} cancel-btn 是否显示底部的取消按钮默认true
* @property {Number String} border-radius 弹出部分顶部左右的圆角值单位rpx默认0
* @property {Boolean} mask-close-able 点击遮罩是否可以关闭默认true
* @property {Boolean} safe-area-inset-bottom 是否开启底部安全区适配默认false
* @property {Number String} z-index z-index值默认1075
* @property {String} cancel-text 取消按钮的提示文字
* @event {Function} click 点击ActionSheet列表项时触发
* @event {Function} close 点击取消按钮时触发
* @example <u-action-sheet :list="list" @click="click" v-model="show"></u-action-sheet>
*/
export default {
name: "u-action-sheet",
props: {
// 点击遮罩是否可以关闭actionsheet
maskCloseAble: {
type: Boolean,
default: true
},
// 按钮的文字数组可以自定义颜色和字体大小字体单位为rpx
list: {
type: Array,
default () {
// 如下
// return [{
// text: '确定',
// color: '',
// fontSize: ''
// }]
return [];
}
},
// 顶部的提示文字
tips: {
type: Object,
default () {
return {
text: '',
color: '',
fontSize: '26'
}
}
},
// 底部的取消按钮
cancelBtn: {
type: Boolean,
default: true
},
// 是否开启底部安全区适配开启的话会在iPhoneX机型底部添加一定的内边距
safeAreaInsetBottom: {
type: Boolean,
default: false
},
// 通过双向绑定控制组件的弹出与收起
value: {
type: Boolean,
default: false
},
// 弹出的顶部圆角值
borderRadius: {
type: [String, Number],
default: 0
},
// 弹出的z-index值
zIndex: {
type: [String, Number],
default: 0
},
// 取消按钮的文字提示
cancelText: {
type: String,
default: '取消'
}
},
computed: {
// 顶部提示的样式
tipsStyle() {
let style = {};
if (this.tips.color) style.color = this.tips.color;
if (this.tips.fontSize) style.fontSize = this.tips.fontSize + 'rpx';
return style;
},
// 操作项目的样式
itemStyle() {
return (index) => {
let style = {};
if (this.list[index].color) style.color = this.list[index].color;
if (this.list[index].fontSize) style.fontSize = this.list[index].fontSize + 'rpx';
// 选项被禁用的样式
if (this.list[index].disabled) style.color = '#c0c4cc';
return style;
}
},
uZIndex() {
// 如果用户有传递z-index值优先使用
return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
}
},
methods: {
// 点击取消按钮
close() {
// 发送input事件并不会作用于父组件而是要设置组件内部通过props传递的value参数
// 这是一个vue发送事件的特殊用法
this.popupClose();
this.$emit('close');
},
// 弹窗关闭
popupClose() {
this.$emit('input', false);
},
// 点击某一个item
itemClick(index) {
// disabled的项禁止点击
if(this.list[index].disabled) return;
this.$emit('click', index);
this.$emit('input', false);
}
}
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-tips {
font-size: 26rpx;
text-align: center;
padding: 34rpx 0;
line-height: 1;
color: $u-tips-color;
}
.u-action-sheet-item {
@include vue-flex;;
line-height: 1;
justify-content: center;
align-items: center;
font-size: 32rpx;
padding: 34rpx 0;
flex-direction: column;
}
.u-action-sheet-item__subtext {
font-size: 24rpx;
color: $u-tips-color;
margin-top: 20rpx;
}
.u-gab {
height: 12rpx;
background-color: rgb(234, 234, 236);
}
.u-actionsheet-cancel {
color: $u-main-color;
}
</style>

Some files were not shown because too many files have changed in this diff Show More