├── .gitignore
├── .hbuilderx
└── launch.json
├── App.vue
├── FILE_PREVIEW.md
├── LICENSE
├── PUSH_MESSAGE.md
├── README.md
├── api
└── api.js
├── colorui
├── animation.css
├── components
│ └── cu-custom.vue
├── icon.css
└── main.css
├── common
├── css
│ ├── common.css
│ ├── custom-dark.less
│ ├── custom-light.less
│ ├── iconfont.css
│ └── uni.css
└── lib
│ ├── md5.min.js
│ └── u-charts.min.js
├── components
├── mescroll-uni
│ ├── components
│ │ ├── mescroll-down.css
│ │ ├── mescroll-down.vue
│ │ ├── mescroll-empty.vue
│ │ ├── mescroll-top.vue
│ │ ├── mescroll-up.css
│ │ └── mescroll-up.vue
│ ├── mescroll-body.css
│ ├── mescroll-body.vue
│ ├── mescroll-mixins.js
│ ├── mescroll-uni-option.js
│ ├── mescroll-uni.css
│ ├── mescroll-uni.js
│ ├── mescroll-uni.vue
│ ├── mixins
│ │ ├── mescroll-comp.js
│ │ ├── mescroll-more-item.js
│ │ └── mescroll-more.js
│ └── wxs
│ │ ├── mixins.js
│ │ ├── renderjs.js
│ │ └── wxs.wxs
├── tui
│ └── tui-datetime.vue
├── uni-badge
│ └── uni-badge.vue
├── uni-card
│ └── uni-card.vue
├── uni-goods-nav
│ └── uni-goods-nav.vue
├── uni-icons
│ └── uni-icons.vue
├── uni-list-item
│ └── uni-list-item.vue
├── uni-list
│ └── uni-list.vue
├── uni-nav-bar
│ └── uni-nav-bar.vue
├── uni-popup
│ └── uni-popup.vue
├── uni-status-bar
│ └── uni-status-bar.vue
├── uni-tag
│ └── uni-tag.vue
└── watch-login
│ ├── css
│ └── icon.css
│ ├── watch-button.vue
│ └── watch-input.vue
├── config
└── index.js
├── hybrid
└── html
│ ├── README.md
│ ├── index.html
│ └── js
│ ├── h5uploader.js
│ └── signclient.js
├── i18n
├── en-US.js
├── index.js
└── zh-CN.js
├── main.js
├── manifest.json
├── package.json
├── pages.json
├── pages
├── analysis
│ ├── index.vue
│ ├── time-table.vue
│ ├── ucharts-demo-data.js
│ └── ucharts-demo.vue
├── file
│ ├── file-preview.vue
│ └── file-upload.vue
├── index
│ ├── audit-idea.vue
│ ├── index.vue
│ ├── project
│ │ ├── adjust-project.vue
│ │ ├── audit-project.vue
│ │ └── detail-project.vue
│ └── user
│ │ ├── audit-user.vue
│ │ └── detail-user.vue
├── login
│ ├── css
│ │ └── main.css
│ ├── forget.vue
│ ├── login.vue
│ └── register.vue
├── project
│ ├── index.vue
│ ├── project-detail.vue
│ └── project-list.vue
├── user
│ ├── index.vue
│ └── setting.vue
└── yzcloud
│ ├── index.vue
│ ├── signclient.js
│ ├── yz-edit.vue
│ └── yz-preview-callback.vue
├── static
├── img
│ ├── backtop.png
│ ├── edit.png
│ ├── logo.png
│ ├── project
│ │ ├── asset.png
│ │ ├── build.png
│ │ ├── land.png
│ │ └── purchase.png
│ ├── tabbar
│ │ ├── home.png
│ │ ├── homeHL.png
│ │ ├── org.png
│ │ ├── orgHL.png
│ │ ├── project.png
│ │ ├── projectHL.png
│ │ ├── todo.png
│ │ ├── todoHL.png
│ │ ├── tongji.png
│ │ ├── tongjiHL.png
│ │ ├── user.png
│ │ ├── userHL.png
│ │ ├── yz.png
│ │ ├── yz2.png
│ │ ├── yzHL.png
│ │ └── yzHL2.png
│ └── user
│ │ └── agencyOrg.png
└── uni.ttf
├── store
├── index.js
└── modules
│ ├── app.js
│ ├── index.js
│ └── user.js
├── uni.scss
├── unipush离线集成指南.md
├── utils
├── MinCache.js
├── MinRequest.js
├── MinRouter.js
├── checkResponse.js
├── datetime.js
├── graceChecker.js
├── index.js
└── url.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .project
3 | unpackage/
4 | .vscode/
5 | .idea
6 | .DS_Store
7 | *.iml
8 |
--------------------------------------------------------------------------------
/.hbuilderx/launch.json:
--------------------------------------------------------------------------------
1 | { // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
2 | // launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数
3 | "version": "0.0",
4 | "configurations": [{
5 | "type": "uniCloud",
6 | "default": {
7 | "launchtype": "remote"
8 | }
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/App.vue:
--------------------------------------------------------------------------------
1 |
139 |
140 |
149 |
--------------------------------------------------------------------------------
/api/api.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import MinRequest from '@/utils/MinRequest'
3 | import globalConfig from '@/config'
4 | import { generateSign } from '@/pages/yzcloud/signclient.js'
5 |
6 | const minRequest = new MinRequest()
7 |
8 | // 请求拦截器
9 | minRequest.interceptors.request((request) => {
10 | return request
11 | })
12 |
13 | // 响应拦截器
14 | minRequest.interceptors.response((response) => {
15 | return response.data
16 | })
17 |
18 | // 设置默认配置
19 | minRequest.setConfig((config) => {
20 | config.baseURL = globalConfig.baseUrl
21 | return config
22 | })
23 |
24 | export default {
25 | // 这里统一管理api请求
26 | apis: {
27 | login(params) {
28 | return minRequest.post('/post/user/login', params)
29 | },
30 | userPwdModify(params) {
31 | return minRequest.post('/post/user/pwd/modify', params)
32 | },
33 | // 项目审批列表
34 | listAuditProject() {
35 | return minRequest.get('/get/audit/project/list')
36 | },
37 | // 用户审批列表
38 | listAuditUser() {
39 | return minRequest.get('/get/audit/user/list')
40 | },
41 | // 文档管理接口:HTTP上传文件
42 | yzEditHttpUploadFile({ fileUrl }) {
43 | const sign = generateSign(globalConfig.yzEditAPPKEY, {"appId": [globalConfig.yzEditAPPID],
44 | "fileUrl": [fileUrl]
45 | })
46 | return minRequest.post('/api/file/http', {
47 | fileUrl,
48 | appId: globalConfig.yzEditAPPID,
49 | sign
50 | }, {
51 | baseURL: globalConfig.yzDmcUrl
52 | })
53 | },
54 | // 格式转换接口
55 | yzConvertFile(params) {
56 | const sign = generateSign(globalConfig.yzFormatConvertAPPKEY, {"appId": [globalConfig.yzFormatConvertAPPID],
57 | "fileVersionId": [params.fileVersionId],
58 | "convertType": [params.convertType],
59 | "destinationName": [params.destinationName]
60 | })
61 | return minRequest.post('/api/convert/file', { ...params,
62 | appId: globalConfig.yzFormatConvertAPPID,
63 | sign
64 | }, {
65 | baseURL: globalConfig.yzEicUrl,
66 | header: {
67 | 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8;'
68 | }
69 | })
70 | }
71 | // 文档管理接口:上传文件
72 | // yzPreviewUploadFile({ file }) {
73 | // const sign = ''
74 | // return minRequest.post('/api/file/upload', {
75 | // file,
76 | // appId: globalConfig.yzPreviewAPPID,
77 | // sign
78 | // }, {
79 | // baseURL: globalConfig.yzDmcUrl
80 | // })
81 | // },
82 | // 永中云预览-在线预览
83 | // yzPreviewFile({ fileVersionId }) {
84 | // const sign = ''
85 | // return minRequest.get('/api/view/file', {
86 | // fileVersionId,
87 | // appId: globalConfig.yzPreviewAPPID,
88 | // sign
89 | // }, {
90 | // baseURL: globalConfig.yzEicUrl
91 | // })
92 | // }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/colorui/animation.css:
--------------------------------------------------------------------------------
1 | /*
2 | Animation 微动画
3 | 基于ColorUI组建库的动画模块 by 文晓港 2019年3月26日19:52:28
4 | */
5 |
6 | /* css 滤镜 控制黑白底色gif的 */
7 | .gif-black{
8 | mix-blend-mode: screen;
9 | }
10 | .gif-white{
11 | mix-blend-mode: multiply;
12 | }
13 |
14 |
15 | /* Animation css */
16 | [class*=animation-] {
17 | animation-duration: .5s;
18 | animation-timing-function: ease-out;
19 | animation-fill-mode: both
20 | }
21 |
22 | .animation-fade {
23 | animation-name: fade;
24 | animation-duration: .8s;
25 | animation-timing-function: linear
26 | }
27 |
28 | .animation-scale-up {
29 | animation-name: scale-up
30 | }
31 |
32 | .animation-scale-down {
33 | animation-name: scale-down
34 | }
35 |
36 | .animation-slide-top {
37 | animation-name: slide-top
38 | }
39 |
40 | .animation-slide-bottom {
41 | animation-name: slide-bottom
42 | }
43 |
44 | .animation-slide-left {
45 | animation-name: slide-left
46 | }
47 |
48 | .animation-slide-right {
49 | animation-name: slide-right
50 | }
51 |
52 | .animation-shake {
53 | animation-name: shake
54 | }
55 |
56 | .animation-reverse {
57 | animation-direction: reverse
58 | }
59 |
60 | @keyframes fade {
61 | 0% {
62 | opacity: 0
63 | }
64 |
65 | 100% {
66 | opacity: 1
67 | }
68 | }
69 |
70 | @keyframes scale-up {
71 | 0% {
72 | opacity: 0;
73 | transform: scale(.2)
74 | }
75 |
76 | 100% {
77 | opacity: 1;
78 | transform: scale(1)
79 | }
80 | }
81 |
82 | @keyframes scale-down {
83 | 0% {
84 | opacity: 0;
85 | transform: scale(1.8)
86 | }
87 |
88 | 100% {
89 | opacity: 1;
90 | transform: scale(1)
91 | }
92 | }
93 |
94 | @keyframes slide-top {
95 | 0% {
96 | opacity: 0;
97 | transform: translateY(-100%)
98 | }
99 |
100 | 100% {
101 | opacity: 1;
102 | transform: translateY(0)
103 | }
104 | }
105 |
106 | @keyframes slide-bottom {
107 | 0% {
108 | opacity: 0;
109 | transform: translateY(100%)
110 | }
111 |
112 | 100% {
113 | opacity: 1;
114 | transform: translateY(0)
115 | }
116 | }
117 |
118 | @keyframes shake {
119 |
120 | 0%,
121 | 100% {
122 | transform: translateX(0)
123 | }
124 |
125 | 10% {
126 | transform: translateX(-9px)
127 | }
128 |
129 | 20% {
130 | transform: translateX(8px)
131 | }
132 |
133 | 30% {
134 | transform: translateX(-7px)
135 | }
136 |
137 | 40% {
138 | transform: translateX(6px)
139 | }
140 |
141 | 50% {
142 | transform: translateX(-5px)
143 | }
144 |
145 | 60% {
146 | transform: translateX(4px)
147 | }
148 |
149 | 70% {
150 | transform: translateX(-3px)
151 | }
152 |
153 | 80% {
154 | transform: translateX(2px)
155 | }
156 |
157 | 90% {
158 | transform: translateX(-1px)
159 | }
160 | }
161 |
162 | @keyframes slide-left {
163 | 0% {
164 | opacity: 0;
165 | transform: translateX(-100%)
166 | }
167 |
168 | 100% {
169 | opacity: 1;
170 | transform: translateX(0)
171 | }
172 | }
173 |
174 | @keyframes slide-right {
175 | 0% {
176 | opacity: 0;
177 | transform: translateX(100%)
178 | }
179 |
180 | 100% {
181 | opacity: 1;
182 | transform: translateX(0)
183 | }
184 | }
--------------------------------------------------------------------------------
/colorui/components/cu-custom.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
66 |
67 |
70 |
--------------------------------------------------------------------------------
/common/css/common.css:
--------------------------------------------------------------------------------
1 | page, uni-page-body, #app {
2 | height: 100%!important;
3 | }
4 |
5 | .w-h-100 {
6 | height: 100%;
7 | width: 100%;
8 | }
9 |
10 | .uni-flex {
11 | display: flex;
12 | }
13 |
14 | .uni-row {
15 | flex-direction: row;
16 | justify-content: flex-start;
17 | align-items: flex-start;
18 | margin: 4rpx 0;
19 | }
20 |
21 | .uni-row .flex-item-20 {
22 | color: #8799a3;
23 | }
24 |
25 | .uni-row .flex-item-30 {
26 | color: #8799a3;
27 | }
28 |
29 | .flex-item-20 {
30 | flex-basis: 20%;
31 | }
32 |
33 | .flex-item-80 {
34 | flex-basis: 80%;
35 | }
36 |
37 | .flex-item-30 {
38 | flex-basis: 30%;
39 | }
40 |
41 | .flex-item-70 {
42 | flex-basis: 70%;
43 | }
44 |
45 | .my-iconfont {
46 | font-size: 24rpx;
47 | }
48 |
49 | .footer-box {
50 | display: flex;
51 | justify-content: space-between;
52 | width: 100%;
53 | padding: 0 10%;
54 | }
55 |
56 | .footer-box__item {
57 | width: 100%;
58 | display: flex;
59 | align-items: center;
60 | }
61 |
62 | .footer-box__item:nth-child(2) {
63 | justify-content: center;
64 | }
65 |
66 | .footer-box__item:last-child {
67 | justify-content: flex-end;
68 | }
69 |
70 | .audit-card-content {
71 | margin: 0 20rpx;
72 | }
73 |
74 | .mycard .card-item {
75 | margin-bottom: 20rpx;
76 | }
77 |
78 | .mycard {
79 | margin-top: 20rpx;
80 | }
81 |
82 | .goods-carts {
83 | width: 100%;
84 | position: fixed;
85 | bottom: 0;
86 | }
87 |
88 | /* 顶部搜索框 */
89 | .input-view {
90 | width: 100%;
91 | display: flex;
92 | background-color: #e7e7e7;
93 | height: 30px;
94 | border-radius: 15px;
95 | padding: 0 4%;
96 | flex-wrap: nowrap;
97 | margin: 7px 10rpx;
98 | line-height: 30px;
99 | background: #f5f5f5;
100 | }
101 |
102 | .input-view .uni-icon {
103 | line-height: 30px !important;
104 | }
105 |
106 | .input-view .input {
107 | height: 30px;
108 | line-height: 30px;
109 | width: 94%;
110 | padding: 0 3%;
111 | }
112 |
113 | .my-tab-bar .cu-item {
114 | height: auto;
115 | margin: 0;
116 | padding: 0;
117 | line-height: 44px;
118 | }
119 |
120 | /* 底部分享 */
121 | .uni-share {
122 | background: #fff;
123 | }
124 |
125 | .uni-share-padding-bottom {
126 | padding-bottom: 22%;
127 | }
128 |
129 | .idea-textarea {
130 | border: 1rpx solid #d9d9d9;
131 | width: 100%;
132 | }
133 |
134 | .uni-share-title {
135 | line-height: 60rpx;
136 | font-size: 24rpx;
137 | padding: 15rpx 0;
138 | text-align: center;
139 | }
140 |
141 | .uni-share-content {
142 | display: flex;
143 | flex-wrap: wrap;
144 | padding: 15px 15px 0 15px;
145 | }
146 |
147 | .uni-share-content-box {
148 | display: flex;
149 | flex-direction: column;
150 | align-items: center;
151 | width: 25%;
152 | box-sizing: border-box;
153 | }
154 |
155 | .uni-share-content-image {
156 | display: flex;
157 | justify-content: center;
158 | align-items: center;
159 | width: 60rpx;
160 | height: 60rpx;
161 | overflow: hidden;
162 | border-radius: 10rpx;
163 | }
164 |
165 | .uni-share-content-image .image {
166 | width: 100%;
167 | height: 100%;
168 | }
169 |
170 | .uni-share-content-text {
171 | font-size: 26rpx;
172 | color: #333;
173 | padding-top: 5px;
174 | padding-bottom: 10px;
175 | }
176 |
177 | .uni-share-btn {
178 | height: 90rpx;
179 | line-height: 90rpx;
180 | border-top: 1px #f5f5f5 solid;
181 | text-align: center;
182 | color: #666;
183 | }
184 |
185 | .uni-timeline-item .uni-timeline-item-keynode {
186 | width: auto;
187 | }
188 |
189 | .uni-timeline-last-item .uni-timeline-item-divider {
190 | background-color: #bbb !important;
191 | }
192 |
193 | .uni-timeline-first-item .uni-timeline-item-divider {
194 | background-color: #1AAD19 !important;
195 | }
196 |
197 | .uni-card__header-extra-text {
198 | width: auto !important;
199 | }
200 |
201 | .scoll-y {
202 | height: 100%;
203 | }
204 |
205 | .tui-tabs-relative::before {
206 | border-bottom: none!important;
207 | }
208 |
--------------------------------------------------------------------------------
/common/css/custom-dark.less:
--------------------------------------------------------------------------------
1 | @dark_bg_main: #0d1015;
2 | @dark_bg: #161a23;
3 | @dark_bg_sub: #2a2b2d;
4 | @dark_text_main: #d3e0f3;
5 | @dark_text_sub: #8c8c8c;
6 | @dark_text_3: #434343;
7 | @dark_text_4: #595959;
8 |
9 | .custom-dark {
10 | background-color: @dark_bg_main!important;
11 | .uni-card {
12 | background-color: @dark_bg!important;
13 | }
14 |
15 | .uni-row .flex-item-80, .uni-row .flex-item-70, .uni-card__header, .uni-card__header-extra-text, .qiun-title-dot-light {
16 | color: @dark_text_main!important;
17 | }
18 |
19 | .uni-card--shadow {
20 | border: 1px solid @dark_text_3!important;
21 | }
22 |
23 | .uni-card__footer {
24 | border-top: 1px solid @dark_text_3!important;
25 | }
26 |
27 | .analysis, .cu-bar, .qiun-columns, .qiun-bg-white, .detail-item {
28 | background-color: @dark_bg!important;
29 | color: @dark_text_main!important;
30 |
31 | .analysis-num {
32 | color: #fff566;
33 | }
34 | }
35 |
36 | .cu-list, .cu-item {
37 | background-color: @dark_bg!important;
38 | color: @dark_text_main!important;
39 | }
40 | .cu-item:after {
41 | border-bottom: 1px solid @dark_text_3!important;
42 | }
43 |
44 | .cu-item.arrow::before {
45 | color: @dark_text_4!important;
46 | }
47 |
48 | .grid-text {
49 | color: @dark_text_main!important;
50 | }
51 |
52 | .main-list {
53 | .cuIcon {
54 | color: @dark_text_main!important;
55 | }
56 | .uni-input-input {
57 | background-color: @dark_bg_sub!important;
58 | color: @dark_text_main!important;
59 | }
60 | }
61 | }
--------------------------------------------------------------------------------
/common/css/custom-light.less:
--------------------------------------------------------------------------------
1 | @light_bg: #ffffff;
2 | @light_text_main: #000000;
3 | @light_text_sub: #666666;
4 |
5 | .custom-light {
6 | .analysis, .cu-bar, .detail-item {
7 | background-color: @light_bg;
8 | color: @light_text_sub;
9 |
10 | .analysis-num {
11 | color: #1890ff;
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/components/mescroll-uni/components/mescroll-down.css:
--------------------------------------------------------------------------------
1 | /* 下拉刷新区域 */
2 | .mescroll-downwarp {
3 | position: absolute;
4 | top: -100%;
5 | left: 0;
6 | width: 100%;
7 | height: 100%;
8 | text-align: center;
9 | }
10 |
11 | /* 下拉刷新--内容区,定位于区域底部 */
12 | .mescroll-downwarp .downwarp-content {
13 | position: absolute;
14 | left: 0;
15 | bottom: 0;
16 | width: 100%;
17 | min-height: 60rpx;
18 | padding: 20rpx 0;
19 | text-align: center;
20 | }
21 |
22 | /* 下拉刷新--提示文本 */
23 | .mescroll-downwarp .downwarp-tip {
24 | display: inline-block;
25 | font-size: 28rpx;
26 | vertical-align: middle;
27 | margin-left: 16rpx;
28 | /* color: gray; 已在style设置color,此处删去*/
29 | }
30 |
31 | /* 下拉刷新--旋转进度条 */
32 | .mescroll-downwarp .downwarp-progress {
33 | display: inline-block;
34 | width: 32rpx;
35 | height: 32rpx;
36 | border-radius: 50%;
37 | border: 2rpx solid gray;
38 | border-bottom-color: transparent !important; /*已在style设置border-color,此处需加 !important*/
39 | vertical-align: middle;
40 | }
41 |
42 | /* 旋转动画 */
43 | .mescroll-downwarp .mescroll-rotate {
44 | animation: mescrollDownRotate 0.6s linear infinite;
45 | }
46 |
47 | @keyframes mescrollDownRotate {
48 | 0% {
49 | transform: rotate(0deg);
50 | }
51 |
52 | 100% {
53 | transform: rotate(360deg);
54 | }
55 | }
--------------------------------------------------------------------------------
/components/mescroll-uni/components/mescroll-down.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{downText}}
7 |
8 |
9 |
10 |
11 |
44 |
45 |
48 |
--------------------------------------------------------------------------------
/components/mescroll-uni/components/mescroll-empty.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 | {{ tip }}
12 | {{ option.btnText }}
13 |
14 |
15 |
16 |
48 |
49 |
91 |
--------------------------------------------------------------------------------
/components/mescroll-uni/components/mescroll-top.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
14 |
49 |
50 |
84 |
--------------------------------------------------------------------------------
/components/mescroll-uni/components/mescroll-up.css:
--------------------------------------------------------------------------------
1 | /* 上拉加载区域 */
2 | .mescroll-upwarp {
3 | box-sizing: border-box;
4 | min-height: 110rpx;
5 | padding: 30rpx 0;
6 | text-align: center;
7 | clear: both;
8 | }
9 |
10 | /*提示文本 */
11 | .mescroll-upwarp .upwarp-tip,
12 | .mescroll-upwarp .upwarp-nodata {
13 | display: inline-block;
14 | font-size: 28rpx;
15 | vertical-align: middle;
16 | /* color: gray; 已在style设置color,此处删去*/
17 | }
18 |
19 | .mescroll-upwarp .upwarp-tip {
20 | margin-left: 16rpx;
21 | }
22 |
23 | /*旋转进度条 */
24 | .mescroll-upwarp .upwarp-progress {
25 | display: inline-block;
26 | width: 32rpx;
27 | height: 32rpx;
28 | border-radius: 50%;
29 | border: 2rpx solid gray;
30 | border-bottom-color: transparent !important; /*已在style设置border-color,此处需加 !important*/
31 | vertical-align: middle;
32 | }
33 |
34 | /* 旋转动画 */
35 | .mescroll-upwarp .mescroll-rotate {
36 | animation: mescrollUpRotate 0.6s linear infinite;
37 | }
38 |
39 | @keyframes mescrollUpRotate {
40 | 0% {
41 | transform: rotate(0deg);
42 | }
43 |
44 | 100% {
45 | transform: rotate(360deg);
46 | }
47 | }
--------------------------------------------------------------------------------
/components/mescroll-uni/components/mescroll-up.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{ mOption.textLoading }}
8 |
9 |
10 | {{ mOption.textNoMore }}
11 |
12 |
13 |
14 |
36 |
37 |
40 |
--------------------------------------------------------------------------------
/components/mescroll-uni/mescroll-body.css:
--------------------------------------------------------------------------------
1 | .mescroll-body {
2 | position: relative; /* 下拉刷新区域相对自身定位 */
3 | height: auto; /* 不可固定高度,否则overflow:hidden导致无法滑动; 同时使设置的最小高生效,实现列表不满屏仍可下拉*/
4 | overflow: hidden; /* 当有元素写在mescroll-body标签前面时,可遮住下拉刷新区域 */
5 | box-sizing: border-box; /* 避免设置padding出现双滚动条的问题 */
6 | }
7 |
8 | /* 使sticky生效: 父元素不能overflow:hidden或者overflow:auto属性 */
9 | .mescroll-body.mescorll-sticky{
10 | overflow: unset !important
11 | }
12 |
13 | /* 适配 iPhoneX */
14 | @supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
15 | .mescroll-safearea {
16 | padding-bottom: constant(safe-area-inset-bottom);
17 | padding-bottom: env(safe-area-inset-bottom);
18 | }
19 | }
--------------------------------------------------------------------------------
/components/mescroll-uni/mescroll-mixins.js:
--------------------------------------------------------------------------------
1 | // mescroll-body 和 mescroll-uni 通用
2 |
3 | // import MescrollUni from "./mescroll-uni.vue";
4 | // import MescrollBody from "./mescroll-body.vue";
5 |
6 | const MescrollMixin = {
7 | // components: { // 非H5端无法通过mixin注册组件, 只能在main.js中注册全局组件或具体界面中注册
8 | // MescrollUni,
9 | // MescrollBody
10 | // },
11 | data() {
12 | return {
13 | mescroll: null //mescroll实例对象
14 | }
15 | },
16 | // 注册系统自带的下拉刷新 (配置down.native为true时生效, 还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
17 | onPullDownRefresh(){
18 | this.mescroll && this.mescroll.onPullDownRefresh();
19 | },
20 | // 注册列表滚动事件,用于判定在顶部可下拉刷新,在指定位置可显示隐藏回到顶部按钮 (此方法为页面生命周期,无法在子组件中触发, 仅在mescroll-body生效)
21 | onPageScroll(e) {
22 | this.mescroll && this.mescroll.onPageScroll(e);
23 | },
24 | // 注册滚动到底部的事件,用于上拉加载 (此方法为页面生命周期,无法在子组件中触发, 仅在mescroll-body生效)
25 | onReachBottom() {
26 | this.mescroll && this.mescroll.onReachBottom();
27 | },
28 | methods: {
29 | // mescroll组件初始化的回调,可获取到mescroll对象
30 | mescrollInit(mescroll) {
31 | this.mescroll = mescroll;
32 | this.mescrollInitByRef(); // 兼容字节跳动小程序
33 | },
34 | // 以ref的方式初始化mescroll对象 (兼容字节跳动小程序)
35 | mescrollInitByRef() {
36 | if(!this.mescroll || !this.mescroll.resetUpScroll){
37 | let mescrollRef = this.$refs.mescrollRef;
38 | if(mescrollRef) this.mescroll = mescrollRef.mescroll
39 | }
40 | },
41 | // 下拉刷新的回调 (mixin默认resetUpScroll)
42 | downCallback() {
43 | if(this.mescroll.optUp.use){
44 | this.mescroll.resetUpScroll()
45 | }else{
46 | setTimeout(()=>{
47 | this.mescroll.endSuccess();
48 | }, 500)
49 | }
50 | },
51 | // 上拉加载的回调
52 | upCallback() {
53 | // mixin默认延时500自动结束加载
54 | setTimeout(()=>{
55 | this.mescroll.endErr();
56 | }, 500)
57 | }
58 | },
59 | mounted() {
60 | this.mescrollInitByRef(); // 兼容字节跳动小程序, 避免未设置@init或@init此时未能取到ref的情况
61 | }
62 |
63 | }
64 |
65 | export default MescrollMixin;
66 |
--------------------------------------------------------------------------------
/components/mescroll-uni/mescroll-uni-option.js:
--------------------------------------------------------------------------------
1 | // 全局配置
2 | // mescroll-body 和 mescroll-uni 通用
3 | const GlobalOption = {
4 | down: {
5 | // 其他down的配置参数也可以写,这里只展示了常用的配置:
6 | textInOffset: '下拉刷新', // 下拉的距离在offset范围内的提示文本
7 | textOutOffset: '释放更新', // 下拉的距离大于offset范围的提示文本
8 | textLoading: '加载中 ...', // 加载中的提示文本
9 | textSuccess: '加载成功', // 加载成功的文本
10 | textErr: '加载失败', // 加载失败的文本
11 | beforeEndDelay: 100, // 延时结束的时长 (显示加载成功/失败的时长)
12 | offset: 80, // 在列表顶部,下拉大于80px,松手即可触发下拉刷新的回调
13 | native: false // 是否使用系统自带的下拉刷新; 默认false; 仅在mescroll-body生效 (值为true时,还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
14 | },
15 | up: {
16 | // 其他up的配置参数也可以写,这里只展示了常用的配置:
17 | textLoading: '加载中 ...', // 加载中的提示文本
18 | textNoMore: '-- END --', // 没有更多数据的提示文本
19 | offset: 150, // 距底部多远时,触发upCallback,仅mescroll-uni生效 ( mescroll-body配置的是pages.json的 onReachBottomDistance )
20 | toTop: {
21 | // 回到顶部按钮,需配置src才显示
22 | src: "https://www.mescroll.com/img/mescroll-totop.png", // 图片路径 (建议放入static目录, 如 /static/img/mescroll-totop.png )
23 | offset: 1000, // 列表滚动多少距离才显示回到顶部按钮,默认1000px
24 | right: 20, // 到右边的距离, 默认20 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
25 | bottom: 120, // 到底部的距离, 默认120 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
26 | width: 72 // 回到顶部图标的宽度, 默认72 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
27 | },
28 | empty: {
29 | use: true, // 是否显示空布局
30 | icon: "https://www.mescroll.com/img/mescroll-empty.png", // 图标路径 (建议放入static目录, 如 /static/img/mescroll-empty.png )
31 | tip: '~ 空空如也 ~' // 提示
32 | }
33 | }
34 | }
35 |
36 | export default GlobalOption
37 |
--------------------------------------------------------------------------------
/components/mescroll-uni/mescroll-uni.css:
--------------------------------------------------------------------------------
1 | .mescroll-uni-warp{
2 | height: 100%;
3 | }
4 |
5 | .mescroll-uni-content{
6 | height: 100%;
7 | }
8 |
9 | .mescroll-uni {
10 | position: relative;
11 | width: 100%;
12 | height: 100%;
13 | min-height: 200rpx;
14 | overflow-y: auto;
15 | box-sizing: border-box; /* 避免设置padding出现双滚动条的问题 */
16 | }
17 |
18 | /* 定位的方式固定高度 */
19 | .mescroll-uni-fixed{
20 | z-index: 1;
21 | position: fixed;
22 | top: 0;
23 | left: 0;
24 | right: 0;
25 | bottom: 0;
26 | width: auto; /* 使right生效 */
27 | height: auto; /* 使bottom生效 */
28 | }
29 |
30 | /* 适配 iPhoneX */
31 | @supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
32 | .mescroll-safearea {
33 | padding-bottom: constant(safe-area-inset-bottom);
34 | padding-bottom: env(safe-area-inset-bottom);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/components/mescroll-uni/mixins/mescroll-comp.js:
--------------------------------------------------------------------------------
1 | /**
2 | * mescroll-body写在子组件时,需通过mescroll的mixins补充子组件缺少的生命周期
3 | */
4 | const MescrollCompMixin = {
5 | // 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件 (一级)
6 | onPageScroll(e) {
7 | this.handlePageScroll(e)
8 | },
9 | onReachBottom() {
10 | this.handleReachBottom()
11 | },
12 | // 当down的native: true时, 还需传递此方法进到子组件
13 | onPullDownRefresh(){
14 | this.handlePullDownRefresh()
15 | },
16 | // mescroll-body写在子子子...组件的情况 (多级)
17 | data() {
18 | return {
19 | mescroll: {
20 | onPageScroll: e=>{
21 | this.handlePageScroll(e)
22 | },
23 | onReachBottom: ()=>{
24 | this.handleReachBottom()
25 | },
26 | onPullDownRefresh: ()=>{
27 | this.handlePullDownRefresh()
28 | }
29 | }
30 | }
31 | },
32 | methods:{
33 | handlePageScroll(e){
34 | let item = this.$refs["mescrollItem"];
35 | if(item && item.mescroll) item.mescroll.onPageScroll(e);
36 | },
37 | handleReachBottom(){
38 | let item = this.$refs["mescrollItem"];
39 | if(item && item.mescroll) item.mescroll.onReachBottom();
40 | },
41 | handlePullDownRefresh(){
42 | let item = this.$refs["mescrollItem"];
43 | if(item && item.mescroll) item.mescroll.onPullDownRefresh();
44 | }
45 | }
46 | }
47 |
48 | export default MescrollCompMixin;
49 |
--------------------------------------------------------------------------------
/components/mescroll-uni/mixins/mescroll-more-item.js:
--------------------------------------------------------------------------------
1 | /**
2 | * mescroll-more-item的mixins, 仅在多个 mescroll-body 写在子组件时使用 (参考 mescroll-more 案例)
3 | */
4 | const MescrollMoreItemMixin = {
5 | // 支付宝小程序不支持props的mixin,需写在具体的页面中
6 | // #ifndef MP-ALIPAY || MP-DINGTALK
7 | props:{
8 | i: Number, // 每个tab页的专属下标
9 | index: { // 当前tab的下标
10 | type: Number,
11 | default(){
12 | return 0
13 | }
14 | }
15 | },
16 | // #endif
17 | data() {
18 | return {
19 | downOption:{
20 | auto:false // 不自动加载
21 | },
22 | upOption:{
23 | auto:false // 不自动加载
24 | },
25 | isInit: false // 当前tab是否已初始化
26 | }
27 | },
28 | watch:{
29 | // 监听下标的变化
30 | index(val){
31 | if (this.i === val && !this.isInit) {
32 | this.isInit = true; // 标记为true
33 | this.mescroll && this.mescroll.triggerDownScroll();
34 | }
35 | }
36 | },
37 | methods: {
38 | // 以ref的方式初始化mescroll对象 (兼容字节跳动小程序)
39 | mescrollInitByRef() {
40 | if(!this.mescroll || !this.mescroll.resetUpScroll){
41 | // 字节跳动小程序编辑器不支持一个页面存在相同的ref, 多mescroll的ref需动态生成, 格式为'mescrollRef下标'
42 | let mescrollRef = this.$refs.mescrollRef || this.$refs['mescrollRef'+this.i];
43 | if(mescrollRef) this.mescroll = mescrollRef.mescroll
44 | }
45 | },
46 | // mescroll组件初始化的回调,可获取到mescroll对象 (覆盖mescroll-mixins.js的mescrollInit, 为了标记isInit)
47 | mescrollInit(mescroll) {
48 | this.mescroll = mescroll;
49 | this.mescrollInitByRef && this.mescrollInitByRef(); // 兼容字节跳动小程序
50 | // 自动加载当前tab的数据
51 | if(this.i === this.index){
52 | this.isInit = true; // 标记为true
53 | this.mescroll.triggerDownScroll();
54 | }
55 | },
56 | }
57 | }
58 |
59 | export default MescrollMoreItemMixin;
60 |
--------------------------------------------------------------------------------
/components/mescroll-uni/mixins/mescroll-more.js:
--------------------------------------------------------------------------------
1 | /**
2 | * mescroll-body写在子组件时, 需通过mescroll的mixins补充子组件缺少的生命周期
3 | */
4 | const MescrollMoreMixin = {
5 | data() {
6 | return {
7 | tabIndex: 0, // 当前tab下标
8 | mescroll: {
9 | onPageScroll: e=>{
10 | this.handlePageScroll(e)
11 | },
12 | onReachBottom: ()=>{
13 | this.handleReachBottom()
14 | },
15 | onPullDownRefresh: ()=>{
16 | this.handlePullDownRefresh()
17 | }
18 | }
19 | }
20 | },
21 | // 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件
22 | onPageScroll(e) {
23 | this.handlePageScroll(e)
24 | },
25 | onReachBottom() {
26 | this.handleReachBottom()
27 | },
28 | // 当down的native: true时, 还需传递此方法进到子组件
29 | onPullDownRefresh(){
30 | this.handlePullDownRefresh()
31 | },
32 | methods:{
33 | handlePageScroll(e){
34 | let mescroll = this.getMescroll(this.tabIndex);
35 | mescroll && mescroll.onPageScroll(e);
36 | },
37 | handleReachBottom(){
38 | let mescroll = this.getMescroll(this.tabIndex);
39 | mescroll && mescroll.onReachBottom();
40 | },
41 | handlePullDownRefresh(){
42 | let mescroll = this.getMescroll(this.tabIndex);
43 | mescroll && mescroll.onPullDownRefresh();
44 | },
45 | // 根据下标获取对应子组件的mescroll
46 | getMescroll(i){
47 | if(!this.mescrollItems) this.mescrollItems = [];
48 | if(!this.mescrollItems[i]) {
49 | // v-for中的refs
50 | let vForItem = this.$refs["mescrollItem"];
51 | if(vForItem){
52 | this.mescrollItems[i] = vForItem[i]
53 | }else{
54 | // 普通的refs,不可重复
55 | this.mescrollItems[i] = this.$refs["mescrollItem"+i];
56 | }
57 | }
58 | let item = this.mescrollItems[i]
59 | return item ? item.mescroll : null
60 | },
61 | // 切换tab,恢复滚动条位置
62 | tabChange(i){
63 | let mescroll = this.getMescroll(i);
64 | if(mescroll){
65 | // 延时(比$nextTick靠谱一些),确保元素已渲染
66 | setTimeout(()=>{
67 | mescroll.scrollTo(mescroll.getScrollTop(),0)
68 | },30)
69 | }
70 | }
71 | }
72 | }
73 |
74 | export default MescrollMoreMixin;
75 |
--------------------------------------------------------------------------------
/components/mescroll-uni/wxs/mixins.js:
--------------------------------------------------------------------------------
1 | // 定义在wxs (含renderjs) 逻辑层的数据和方法, 与视图层相互通信
2 | const WxsMixin = {
3 | data() {
4 | return {
5 | // 传入wxs视图层的数据 (响应式)
6 | wxsProp: {
7 | optDown:{}, // 下拉刷新的配置
8 | scrollTop:0, // 滚动条的距离
9 | bodyHeight:0, // body的高度
10 | isDownScrolling:false, // 是否正在下拉刷新中
11 | isUpScrolling:false, // 是否正在上拉加载中
12 | isScrollBody:true, // 是否为mescroll-body滚动
13 | isUpBoth:true, // 上拉加载时,是否同时可以下拉刷新
14 | t: 0 // 数据更新的标记 (只有数据更新了,才会触发wxs的Observer)
15 | },
16 |
17 | // 标记调用wxs视图层的方法
18 | callProp: {
19 | callType: '', // 方法名
20 | t: 0 // 数据更新的标记 (只有数据更新了,才会触发wxs的Observer)
21 | },
22 |
23 | // 不用wxs的平台使用此处的wxsBiz对象,抹平wxs的写法 (微信小程序和APP使用的wxsBiz对象是./wxs/wxs.wxs)
24 | // #ifndef MP-WEIXIN || MP-QQ || APP-PLUS || H5
25 | wxsBiz: {
26 | //注册列表touchstart事件,用于下拉刷新
27 | touchstartEvent: e=> {
28 | this.mescroll.touchstartEvent(e);
29 | },
30 | //注册列表touchmove事件,用于下拉刷新
31 | touchmoveEvent: e=> {
32 | this.mescroll.touchmoveEvent(e);
33 | },
34 | //注册列表touchend事件,用于下拉刷新
35 | touchendEvent: e=> {
36 | this.mescroll.touchendEvent(e);
37 | },
38 | propObserver(){}, // 抹平wxs的写法
39 | callObserver(){} // 抹平wxs的写法
40 | },
41 | // #endif
42 |
43 | // 不用renderjs的平台使用此处的renderBiz对象,抹平renderjs的写法 (app 和 h5 使用的renderBiz对象是./wxs/renderjs.js)
44 | // #ifndef APP-PLUS || H5
45 | renderBiz: {
46 | propObserver(){} // 抹平renderjs的写法
47 | }
48 | // #endif
49 | }
50 | },
51 | methods: {
52 | // wxs视图层调用逻辑层的回调
53 | wxsCall(msg){
54 | if(msg.type === 'setWxsProp'){
55 | // 更新wxsProp数据 (值改变才触发更新)
56 | this.wxsProp = {
57 | optDown: this.mescroll.optDown,
58 | scrollTop: this.mescroll.getScrollTop(),
59 | bodyHeight: this.mescroll.getBodyHeight(),
60 | isDownScrolling: this.mescroll.isDownScrolling,
61 | isUpScrolling: this.mescroll.isUpScrolling,
62 | isUpBoth: this.mescroll.optUp.isBoth,
63 | isScrollBody:this.mescroll.isScrollBody,
64 | t: Date.now()
65 | }
66 | }else if(msg.type === 'setLoadType'){
67 | // 设置inOffset,outOffset的状态
68 | this.downLoadType = msg.downLoadType
69 | // 状态挂载到mescroll对象, 以便在其他组件中使用, 比如中
70 | this.$set(this.mescroll, 'downLoadType', this.downLoadType)
71 | // 重置是否加载成功的状态
72 | this.$set(this.mescroll, 'isDownEndSuccess', null)
73 | }else if(msg.type === 'triggerDownScroll'){
74 | // 主动触发下拉刷新
75 | this.mescroll.triggerDownScroll();
76 | }else if(msg.type === 'endDownScroll'){
77 | // 结束下拉刷新
78 | this.mescroll.endDownScroll();
79 | }else if(msg.type === 'triggerUpScroll'){
80 | // 主动触发上拉加载
81 | this.mescroll.triggerUpScroll(true);
82 | }
83 | }
84 | },
85 | mounted() {
86 | // #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5
87 | // 配置主动触发wxs显示加载进度的回调
88 | this.mescroll.optDown.afterLoading = ()=>{
89 | this.callProp = {callType: "showLoading", t: Date.now()} // 触发wxs的方法 (值改变才触发更新)
90 | }
91 | // 配置主动触发wxs隐藏加载进度的回调
92 | this.mescroll.optDown.afterEndDownScroll = ()=>{
93 | this.callProp = {callType: "endDownScroll", t: Date.now()} // 触发wxs的方法 (值改变才触发更新)
94 | let delay = 300 + (this.mescroll.optDown.beforeEndDelay || 0)
95 | setTimeout(()=>{
96 | if(this.downLoadType === 4 || this.downLoadType === 0){
97 | this.callProp = {callType: "clearTransform", t: Date.now()} // 触发wxs的方法 (值改变才触发更新)
98 | }
99 | // 状态挂载到mescroll对象, 以便在其他组件中使用, 比如中
100 | this.$set(this.mescroll, 'downLoadType', this.downLoadType)
101 | }, delay)
102 | }
103 | // 初始化wxs的数据
104 | this.wxsCall({type: 'setWxsProp'})
105 | // #endif
106 | }
107 | }
108 |
109 | export default WxsMixin;
110 |
--------------------------------------------------------------------------------
/components/mescroll-uni/wxs/renderjs.js:
--------------------------------------------------------------------------------
1 | // 使用renderjs直接操作window对象,实现动态控制app和h5的bounce
2 | // bounce: iOS橡皮筋,Android半月弧,h5浏览器下拉背景等效果 (下拉刷新时禁止)
3 | // https://uniapp.dcloud.io/frame?id=renderjs
4 |
5 | // 与wxs的me实例一致
6 | var me = {}
7 |
8 | // 初始化window对象的touch事件 (仅初始化一次)
9 | if(window && !window.$mescrollRenderInit){
10 | window.$mescrollRenderInit = true
11 |
12 |
13 | window.addEventListener('touchstart', function(e){
14 | if (me.disabled()) return;
15 | me.startPoint = me.getPoint(e); // 记录起点
16 | }, {passive: true})
17 |
18 |
19 | window.addEventListener('touchmove', function(e){
20 | if (me.disabled()) return;
21 | if (me.getScrollTop() > 0) return; // 需在顶部下拉,才禁止bounce
22 |
23 | var curPoint = me.getPoint(e); // 当前点
24 | var moveY = curPoint.y - me.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
25 | // 向下拉
26 | if (moveY > 0) {
27 | // 可下拉的条件
28 | if (!me.isDownScrolling && !me.optDown.isLock && (!me.isUpScrolling || (me.isUpScrolling && me.isUpBoth))) {
29 |
30 | // 只有touch在mescroll的view上面,才禁止bounce
31 | var el = e.target;
32 | var isMescrollTouch = false;
33 | while (el && el.tagName && el.tagName !== 'UNI-PAGE-BODY' && el.tagName != "BODY") {
34 | var cls = el.classList;
35 | if (cls && cls.contains('mescroll-render-touch')) {
36 | isMescrollTouch = true
37 | break;
38 | }
39 | el = el.parentNode; // 继续检查其父元素
40 | }
41 | // 禁止bounce (不会对swiper和iOS侧滑返回造成影响)
42 | if (isMescrollTouch && e.cancelable && !e.defaultPrevented) e.preventDefault();
43 | }
44 | }
45 | }, {passive: false})
46 | }
47 |
48 | /* 获取滚动条的位置 */
49 | me.getScrollTop = function() {
50 | return me.scrollTop || 0
51 | }
52 |
53 | /* 是否禁用下拉刷新 */
54 | me.disabled = function(){
55 | return !me.optDown || !me.optDown.use || me.optDown.native
56 | }
57 |
58 | /* 根据点击滑动事件获取第一个手指的坐标 */
59 | me.getPoint = function(e) {
60 | if (!e) {
61 | return {x: 0,y: 0}
62 | }
63 | if (e.touches && e.touches[0]) {
64 | return {x: e.touches[0].pageX,y: e.touches[0].pageY}
65 | } else if (e.changedTouches && e.changedTouches[0]) {
66 | return {x: e.changedTouches[0].pageX,y: e.changedTouches[0].pageY}
67 | } else {
68 | return {x: e.clientX,y: e.clientY}
69 | }
70 | }
71 |
72 | /**
73 | * 监听逻辑层数据的变化 (实时更新数据)
74 | */
75 | function propObserver(wxsProp) {
76 | me.optDown = wxsProp.optDown
77 | me.scrollTop = wxsProp.scrollTop
78 | me.isDownScrolling = wxsProp.isDownScrolling
79 | me.isUpScrolling = wxsProp.isUpScrolling
80 | me.isUpBoth = wxsProp.isUpBoth
81 | }
82 |
83 | /* 导出模块 */
84 | const renderBiz = {
85 | data() {
86 | return {
87 | propObserver: propObserver,
88 | }
89 | }
90 | }
91 |
92 | export default renderBiz;
--------------------------------------------------------------------------------
/components/uni-badge/uni-badge.vue:
--------------------------------------------------------------------------------
1 |
2 | {{ text }}
4 |
5 |
6 |
34 |
35 |
102 |
--------------------------------------------------------------------------------
/components/uni-card/uni-card.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
9 |
12 | {{ title }}
15 |
16 |
19 |
22 |
23 | {{ title }}
24 |
25 |
26 |
27 |
28 |
42 |
43 |
44 |
47 |
48 |
49 |
50 |
57 |
58 |
59 |
60 |
106 |
107 |
291 |
--------------------------------------------------------------------------------
/components/uni-goods-nav/uni-goods-nav.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
16 |
17 | {{ item.text }}
18 |
19 |
23 | {{ item.info }}
24 |
25 |
26 |
27 |
28 |
31 | {{ item.text }}
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
100 |
101 |
198 |
--------------------------------------------------------------------------------
/components/uni-list-item/uni-list-item.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
16 |
21 |
22 |
23 | {{ title }}
24 | {{ note }}
27 |
28 |
29 |
48 |
49 |
50 |
51 |
52 |
53 |
140 |
141 |
246 |
--------------------------------------------------------------------------------
/components/uni-list/uni-list.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
11 |
46 |
--------------------------------------------------------------------------------
/components/uni-nav-bar/uni-nav-bar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
57 |
58 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
133 |
134 |
225 |
--------------------------------------------------------------------------------
/components/uni-popup/uni-popup.vue:
--------------------------------------------------------------------------------
1 |
2 |
20 |
21 |
22 |
95 |
207 |
--------------------------------------------------------------------------------
/components/uni-status-bar/uni-status-bar.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
18 |
19 |
27 |
--------------------------------------------------------------------------------
/components/uni-tag/uni-tag.vue:
--------------------------------------------------------------------------------
1 |
2 |
15 | {{ text }}
16 |
17 |
18 |
19 |
69 |
70 |
164 |
--------------------------------------------------------------------------------
/components/watch-login/watch-button.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
13 |
41 |
42 |
144 |
--------------------------------------------------------------------------------
/components/watch-login/watch-input.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
13 |
14 |
20 |
21 | {{ getVerCodeSecond }}
26 |
27 |
28 |
29 |
30 |
162 |
163 |
205 |
--------------------------------------------------------------------------------
/config/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ip地址或域名
3 | */
4 | const ipAddress = 'http://113.62.127.199:38080/app/mock/16'
5 | // 文件访问地址
6 | const fileAddr = 'http://localhost:8082/fileUpload/'
7 |
8 | // 永中云服务
9 | // 文档管理接口url
10 | const yzDmc = 'http://dmc.yozocloud.cn'
11 | const yzEic = 'http://eic.yozocloud.cn'
12 | // 云预览应用信息
13 | const yzPreviewAPPID = 'appId'
14 | const yzPreviewAPPKEY = 'appKey'
15 | // 云编辑应用信息
16 | const yzEditAPPID = 'appId'
17 | const yzEditAPPKEY = 'appKey'
18 | // 格式转换应用信息
19 | const yzFormatConvertAPPID = 'appId'
20 | const yzFormatConvertAPPKEY = 'appKey'
21 | /**
22 | * api前缀
23 | */
24 | const apiPrefix = '/apiUA'
25 | const apiYzDmc = '/apiYzDmc'
26 | const apiYzEic = '/apiYzEic'
27 | /**
28 | * 针对不同平台的baseUrl
29 | */
30 | const getBaseUrl = () => {
31 | // #ifdef H5
32 | return apiPrefix
33 | // #endif
34 | // #ifndef H5
35 | return ipAddress
36 | // #endif
37 | }
38 | const getYzDmc = () => {
39 | // #ifdef H5
40 | return apiYzDmc
41 | // #endif
42 | // #ifndef H5
43 | return yzDmc
44 | // #endif
45 | }
46 | const getYzEic = () => {
47 | // #ifdef H5
48 | return apiYzEic
49 | // #endif
50 | // #ifndef H5
51 | return yzEic
52 | // #endif
53 | }
54 | export default {
55 | /**
56 | * 针对不同平台的baseUrl
57 | */
58 | baseUrl: getBaseUrl(),
59 | fileAddr,
60 | yzDmcUrl: getYzDmc(),
61 | yzEic,
62 | yzEicUrl: getYzEic(),
63 | yzPreviewAPPID,
64 | yzPreviewAPPKEY,
65 | yzEditAPPID,
66 | yzEditAPPKEY,
67 | yzFormatConvertAPPID,
68 | yzFormatConvertAPPKEY
69 | }
70 |
--------------------------------------------------------------------------------
/hybrid/html/README.md:
--------------------------------------------------------------------------------
1 | ## web-view加载本地html
2 | > 参考https://uniapp.dcloud.io/component/web-view
--------------------------------------------------------------------------------
/hybrid/html/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | H5文档上传
7 |
8 |
9 |
10 | H5文档上传
11 |
12 |
13 |
14 |
15 |
16 |
20 |
21 |
22 |
23 |
24 |
25 |
125 |
126 |
127 |
128 |
--------------------------------------------------------------------------------
/hybrid/html/js/h5uploader.js:
--------------------------------------------------------------------------------
1 | /**!
2 | * h5uploader.js
3 | * This is a simple file upload plugin depends on HTML5.
4 | * You can use it to mordern browser.
5 | *
6 | * version: 1.0
7 | * Copyright 2015, Ziv | http://imziv.com
8 | * Released under the MIT License
9 | * https://github.com/wewoor/h5uploader
10 | **/
11 |
12 | (function(window) {
13 |
14 | window.H5Uploader = (function() {
15 |
16 | function createXhr() {
17 | if (typeof XMLHttpRequest != "undefined") {
18 | return new XMLHttpRequest();
19 | } else if (typeof ActiveXObject != "undefined") {
20 | if (typeof arguments.callee.activeXString != "string") {
21 | var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"];
22 | for (var i = 0, len = versions.length; i < len; i++) {
23 | try {
24 | new ActiveXObject(versions[i]);
25 | arguments.callee.activeXString = versions[i];
26 | break;
27 | } catch (e) {
28 | throw new Error('Create XHR ActiveXObject error.' + e);
29 | }
30 | }
31 | }
32 | return new ActiveXObject(arguments.callee.activeXString);
33 | } else throw new Error('No XHR object avaliable.' + e);
34 | }
35 |
36 | return {
37 | upload: function(literals) {
38 | if (Object.prototype.toString.call(literals) !== '[object Array]') {
39 | this.handUpload(literals);
40 | } else {
41 | for (var i = 0; i < literals.length; i++) {
42 | this.handUpload(literals[i]);
43 | }
44 | }
45 | },
46 |
47 | handUpload: function(literals) {
48 |
49 | if (literals.action === undefined) {
50 | throw new Error('The upload action address option is undefined.');
51 | }
52 |
53 | var xhr = createXhr();
54 | xhr.open("POST", literals.action, true);
55 | // xhr.setRequestHeader("Content-Type", "multipart/form-data");
56 | // xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
57 | xhr.onreadystatechange = function() {
58 | if (xhr.readyState == 4) {
59 | var body = xhr.responseText;
60 | if (xhr.status >= 200 && xhr.status < 300 ||
61 | xhr.status == 304) {
62 | if (literals.success) {
63 | literals.success(body);
64 | }
65 | } else {
66 | if (literals.fail) {
67 | literals.fail(body);
68 | }
69 | }
70 | }
71 | };
72 |
73 | if (!literals.id) {
74 | throw new Error('The upload id option is undefined.');
75 | }
76 | // var myForm = document.getElementById(literals.formId);
77 | var data = new FormData();
78 | var file = document.getElementById(literals.id);
79 | if (!file) {
80 | throw new Error('The upload file element is undefined::id:' + literals.id);
81 | }
82 | var name = file.getAttribute('name');
83 | if (!name) {
84 | throw new Error('The upload file input name is undefined.');
85 | }
86 |
87 | if (literals.size) { // Check file Size
88 | var evt = this.checkSize(file.files, literals.size.max);
89 | if (evt) {
90 | if (literals.size.valide) literals.size.valide(evt);
91 | throw new Error('The upload file size exceed max value.');
92 | }
93 | }
94 |
95 | if (literals.type) { // Check file type
96 | var evt1 = this.checkType(file.files, literals.type.name);
97 | if (evt1) {
98 | if (literals.type.valide) literals.type.valide(evt1);
99 | throw new Error('The upload file type is error.');
100 | }
101 | }
102 |
103 | if (literals.progress) { // Progress
104 | literals.progress();
105 | }
106 |
107 | for (var i = 0; i < file.files.length; i++) {
108 | data.append(name, file.files[i]);
109 | }
110 |
111 | try {
112 | xhr.send(data);
113 | } catch (e) {
114 | throw new Error(e);
115 | }
116 | },
117 |
118 | // Validate file size
119 | checkSize: function(file, size) {
120 | for (var i = 0; i < file.length; i++) {
121 | if (file[i].size > size * 1024) { // bytes
122 | return file[i];
123 | }
124 | }
125 | },
126 |
127 | // Validate file type
128 | checkType: function(file, type) {
129 | for (var i = 0; i < file.length; i++) {
130 | var arr = file[i].name.split(".");
131 | if (type.indexOf(arr[arr.length - 1]) === -1) {
132 | return file[i];
133 | }
134 | }
135 | }
136 | };
137 | })();
138 |
139 | })(window);
140 |
--------------------------------------------------------------------------------
/hybrid/html/js/signclient.js:
--------------------------------------------------------------------------------
1 | function generateSign(secret, params) {
2 | var fullParamStr = uniqSortParams(params);
3 | return hmacSHA256(fullParamStr, secret);
4 | }
5 |
6 | function uniqSortParams(params) {
7 | delete params.sign;
8 |
9 | var var5 = [];
10 | var var6 = 0;
11 | for (var key in params) {
12 | var5[var6] = key;
13 | var6++;
14 | }
15 | var5.sort(function (a, b) {
16 | return a.localeCompare(b, 'zh-CN');
17 | });
18 |
19 | var result = "";
20 | for (var var7 = 0; var7 < var5.length; var7++) {
21 | var key = var5[var7]
22 | var var8 = params[key];
23 | var8.sort(function (a, b) {
24 | return a.localeCompare(b, 'zh-CN');
25 | });
26 |
27 | if (var8 != null && var8.length > 0) {
28 | for (var var9 = 0; var9 < var8.length; var9++) {
29 | result += key + "=" + var8[var9];
30 | }
31 | } else {
32 | result += key + "=";
33 | }
34 | }
35 | return result;
36 | }
37 |
38 | function hmacSHA256(data, key) {
39 | data != null ? data : "";
40 | var var2 = CryptoJS.HmacSHA256(data, key);
41 | var var3 = var2.toString(CryptoJS.enc.Hex);
42 | return var3.toUpperCase();
43 | }
44 |
--------------------------------------------------------------------------------
/i18n/en-US.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'ProjectApproval': 'Project Approval',
3 | 'ProjectAdjust': 'Project Adjust',
4 | 'UserApproval': 'User Approval',
5 | 'FilePreview': 'File Previewing',
6 | 'Project': 'Project',
7 | 'YzCloud': 'Yozo Cloud',
8 | 'YzDocPreview': 'Yozo Document Preview',
9 | 'YzDocEdit': 'Yozo Document Edit',
10 | 'YzDocPreviewEdit': 'Yozo Document Preview/Edit',
11 | 'DocUpload': 'Document Upload',
12 | 'ProjectDetail': 'Project in detail',
13 | 'Statistics': 'Statistics',
14 | 'Profile': 'Profile',
15 | 'ToDo': 'ToDo',
16 | 'Language': 'Language',
17 | 'Empty': 'Empty',
18 | 'HardLoading': 'Hard loading',
19 | 'NoMore': 'No more',
20 | 'BasicInfo': 'Basic information',
21 | 'Ok': 'Ok',
22 | 'ClearSearchHistory': 'Clear search history',
23 | 'Settings': 'Settings',
24 | 'SignOut': 'Sign out',
25 | 'ChangePassword': 'Change password',
26 | 'Upgrade': 'Upgrade',
27 | 'UserName': 'UserName',
28 | 'Name': 'Name',
29 | 'Role': 'Role',
30 | 'RegisteredUser': 'Registered user',
31 | 'ProjectThisMonth': 'Project this month',
32 | 'System': 'System',
33 | 'English': 'English',
34 | 'Chinese': 'Chinese',
35 | 'en': 'English',
36 | 'zh-CN': 'Chinese',
37 | 'Theme': 'Theme',
38 | 'BluishGreen': 'bluish green',
39 | 'Blush': 'blush',
40 | 'Volcano': 'volcano',
41 | 'Orange': 'Orange',
42 | 'IndigoBlue': 'indigo-blue',
43 | 'Indigo': 'indigo',
44 | 'Verdant': 'verdant',
45 | 'Water': 'water',
46 | 'Grey': 'grey',
47 | 'PinkGold': 'pink gold',
48 | 'Dim': 'dim',
49 | 'Verdigris': 'verdigris',
50 | 'DeepBlack': 'deep black',
51 | 'AgateGreen': 'agate green',
52 | 'DarkGreen': 'dark green',
53 | 'DarkMode': 'dark mode',
54 | 'Dark': 'Dark',
55 | 'ToLogin': 'To login'
56 | }
57 |
--------------------------------------------------------------------------------
/i18n/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import VueI18n from 'vue-i18n'
3 | import enUS from './en-US'
4 | import zhCN from './zh-CN'
5 | Vue.use(VueI18n)
6 |
7 | let lang = uni.getStorageSync('_lang').data
8 | if (!lang || lang === 'System') {
9 | const res = uni.getSystemInfoSync()
10 | lang = res.language
11 | uni.setStorageSync('_lang', 'System')
12 | }
13 |
14 | const i18n = new VueI18n({
15 | locale: lang,
16 | fallbackLocale: 'en',
17 | silentFallbackWarn: true,
18 | messages: {
19 | 'en': enUS,
20 | 'zh-CN': zhCN
21 | }
22 | })
23 | export default i18n
24 |
--------------------------------------------------------------------------------
/i18n/zh-CN.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'ProjectApproval': '项目审批',
3 | 'ProjectAdjust': '项目调整',
4 | 'UserApproval': '用户审批',
5 | 'FilePreview': '文件预览',
6 | 'Project': '项目',
7 | 'YzCloud': '永中云服务',
8 | 'YzDocPreview': '永中云预览',
9 | 'YzDocEdit': '永中云编辑',
10 | 'YzDocPreviewEdit': '永中云预览/云编辑',
11 | 'DocUpload': '文档上传',
12 | 'ProjectDetail': '项目详细',
13 | 'Statistics': '统计',
14 | 'Profile': '我的',
15 | 'ToDo': '待办',
16 | 'Language': '语言',
17 | 'Empty': '空',
18 | 'HardLoading': '努力加载中',
19 | 'NoMore': '没有了',
20 | 'BasicInfo': '基本信息',
21 | 'UserName': '用户名',
22 | 'Name': '姓名',
23 | 'Role': '角色',
24 | 'RegisteredUser': '注册用户',
25 | 'ProjectThisMonth': '本月项目',
26 | 'Ok': '确认',
27 | 'ClearSearchHistory': '清空搜索历史',
28 | 'Settings': '软件设置',
29 | 'SignOut': '退出登录',
30 | 'ChangePassword': '修改密码',
31 | 'Upgrade': '软件升级',
32 | 'System': '跟随系统',
33 | 'English': '英文',
34 | 'Chinese': '中文',
35 | 'en': '英文',
36 | 'zh-CN': '中文',
37 | 'Theme': '主题',
38 | 'BluishGreen': '青碧',
39 | 'Blush': '酡颜',
40 | 'Volcano': '火山',
41 | 'Orange': '橘黄',
42 | 'IndigoBlue': '靛青',
43 | 'Indigo': '靛蓝',
44 | 'Verdant': '苍翠',
45 | 'Water': '水色',
46 | 'Grey': '灰色',
47 | 'PinkGold': '赤金',
48 | 'Dim': '黯',
49 | 'Verdigris': '铜绿',
50 | 'DeepBlack': '玄青',
51 | 'AgateGreen': '湖绿',
52 | 'DarkGreen': '苍色',
53 | 'DarkMode': '深色模式',
54 | 'Dark': '暗黑',
55 | 'ToLogin': '去登录'
56 | }
57 |
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App'
3 | import store from './store'
4 | import i18n from './i18n'
5 | import _ from 'lodash'
6 | import MinRequest from './utils//MinRequest'
7 | import minApi from './api/api'
8 | import MinCache from './utils/MinCache'
9 | // mescroll-body
10 | import MescrollBody from "@/components/mescroll-uni/mescroll-body.vue"
11 |
12 | Vue.config.productionTip = false
13 | Vue.prototype.$store = store
14 | Vue.use(MinRequest)
15 | // 注册缓存器
16 | Vue.use(MinCache)
17 | // mescroll-body
18 | Vue.component('mescroll-body', MescrollBody)
19 |
20 | App.mpType = 'app'
21 | Vue.prototype._i18n = i18n
22 | Vue.prototype.$_ = _
23 |
24 | const app = new Vue({
25 | store,
26 | minApi,
27 | i18n,
28 | ...App
29 | })
30 | app.$mount()
31 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "uniapp-admin",
3 | "version": "2.1.0",
4 | "main": "index.js",
5 | "repository": "https://github.com/silianpan/uniapp-admin.git",
6 | "author": "silianpan ",
7 | "license": "MIT",
8 | "dependencies": {
9 | "crypto-js": "^4.0.0",
10 | "lodash": "^4.17.21",
11 | "vue-i18n": "^8.23.0"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/pages.json:
--------------------------------------------------------------------------------
1 | {
2 | "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
3 | {
4 | "path": "pages/index/index",
5 | "style": {
6 | "app-plus": {
7 | "bounce": "none" // 可选: 是否禁止iOS回弹和Android触顶触底的弧形阴影, 默认允许 (可配在 'globalStyle')
8 | },
9 | "mp-alipay": {
10 | "allowsBounceVertical": "NO" // 可选: 取消支付宝和钉钉小程序的iOS回弹 (可配在 'globalStyle')
11 | }
12 | }
13 | },
14 | {
15 | "path": "pages/login/login",
16 | "style": {
17 | "titleNView": false
18 | }
19 | },
20 | {
21 | "path": "pages/index/project/detail-project"
22 | },
23 | {
24 | "path": "pages/index/project/adjust-project"
25 | },
26 | {
27 | "path": "pages/index/user/detail-user"
28 | },
29 | {
30 | "path": "pages/file/file-preview"
31 | },
32 | {
33 | "path": "pages/project/index"
34 | },
35 | {
36 | "path": "pages/project/project-detail"
37 | },
38 | {
39 | "path": "pages/analysis/index"
40 | },
41 | {
42 | "path": "pages/user/index"
43 | },
44 | {
45 | "path": "pages/user/setting"
46 | },
47 | {
48 | "path": "pages/yzcloud/index"
49 | },
50 | {
51 | "path": "pages/file/file-upload"
52 | },
53 | {
54 | "path": "pages/yzcloud/yz-preview-callback"
55 | },
56 | {
57 | "path": "pages/yzcloud/yz-edit"
58 | }
59 | ],
60 | "tabBar": {
61 | "color": "#8a8a8a",
62 | "selectedColor": "#4364F7",
63 | "backgroundColor": "#ffffff",
64 | "list": [{
65 | "pagePath": "pages/index/index",
66 | "iconPath": "static/img/tabbar/todo.png",
67 | "selectedIconPath": "static/img/tabbar/todoHL.png"
68 | }, {
69 | "pagePath": "pages/yzcloud/index",
70 | "iconPath": "static/img/tabbar/yz.png",
71 | "selectedIconPath": "static/img/tabbar/yzHL.png"
72 | }, {
73 | "pagePath": "pages/project/index",
74 | "iconPath": "static/img/tabbar/project.png",
75 | "selectedIconPath": "static/img/tabbar/projectHL.png"
76 | }, {
77 | "pagePath": "pages/analysis/index",
78 | "iconPath": "static/img/tabbar/tongji.png",
79 | "selectedIconPath": "static/img/tabbar/tongjiHL.png"
80 | }, {
81 | "pagePath": "pages/user/index",
82 | "iconPath": "static/img/tabbar/user.png",
83 | "selectedIconPath": "static/img/tabbar/userHL.png"
84 | }]
85 | },
86 | "globalStyle": {
87 | // "navigationBarTextStyle": "white",
88 | // "navigationBarTitleText": "uniapp-admin",
89 | // "navigationBarBackgroundColor": "#4364F7",
90 | // "backgroundColor": "#F8F8F8"
91 | // "mp-alipay": {
92 | // /* 支付宝小程序特有相关 */
93 | // "transparentTitle": "always",
94 | // "allowsBounceVertical": "NO"
95 | // },
96 | // "navigationBarBackgroundColor": "#0081ff",
97 | // "navigationBarTitleText": "uniapp-admin",
98 | // "navigationStyle": "custom",
99 | // "navigationBarTextStyle": "white"
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/pages/analysis/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{userNum}}
7 |
8 | {{$t('RegisteredUser')}}
9 |
10 |
11 |
12 | {{projectNum}}
13 |
14 | {{$t('ProjectThisMonth')}}
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
64 |
--------------------------------------------------------------------------------
/pages/analysis/time-table.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{value.title}}
8 |
9 |
10 |
21 |
22 |
23 |
24 |
25 |
121 |
122 |
127 |
--------------------------------------------------------------------------------
/pages/analysis/ucharts-demo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 柱状图
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
98 |
99 |
160 |
--------------------------------------------------------------------------------
/pages/file/file-preview.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
49 |
--------------------------------------------------------------------------------
/pages/file/file-upload.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
61 |
--------------------------------------------------------------------------------
/pages/index/audit-idea.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 | 填写处理反馈(非必填)
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
110 |
--------------------------------------------------------------------------------
/pages/index/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | {{item.name}}
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
177 |
178 |
246 |
--------------------------------------------------------------------------------
/pages/index/project/audit-project.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
10 |
11 |
12 | 项目名称
13 | {{item.projectName}}
14 |
15 |
16 | 项目类型
17 | {{item.projectType}}
18 |
19 |
20 | 所在区域
21 | {{item.area}}
22 |
23 |
24 | 项目地址
25 | {{item.addr}}
26 |
27 |
28 | 立项人
29 | {{item.createUser}}
30 |
31 |
32 | 审核状态
33 |
34 |
36 |
37 |
38 |
39 | 附件
40 |
41 |
42 |
43 | {{atta.name}}
44 |
45 |
46 |
47 |
48 |
49 |
50 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
155 |
156 |
161 |
--------------------------------------------------------------------------------
/pages/index/user/audit-user.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
9 |
10 |
11 | 单位名称
12 | {{item.orgName}}
13 |
14 |
15 | 组织机构代码
16 | {{item.orgNo}}
17 |
18 |
19 | 单位类别
20 | {{item.orgType}}
21 |
22 |
23 | 法定代表人
24 | {{item.orgRepresent}}
25 |
26 |
27 | 注册地
28 | {{item.orgRegAddress}}
29 |
30 |
31 | 审核状态
32 |
33 |
35 |
36 |
37 |
38 | 附件
39 |
40 |
41 |
42 | {{atta.name}}
43 |
44 |
45 |
46 |
47 |
48 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
145 |
--------------------------------------------------------------------------------
/pages/login/css/main.css:
--------------------------------------------------------------------------------
1 | .content {
2 | display: flex;
3 | flex-direction: column;
4 | justify-content:center;
5 | /* margin-top: 128rpx; */
6 | }
7 |
8 | /* 头部 logo */
9 | .header {
10 | /* width:161rpx; */
11 | /* height:161rpx; */
12 | /* box-shadow:0rpx 0rpx 60rpx 0rpx rgba(0,0,0,0.1); */
13 | /* border-radius:50%; */
14 | /* background-color: #000000; */
15 | margin-top: 128rpx;
16 | margin-bottom: 72rpx;
17 | margin-left: auto;
18 | margin-right: auto;
19 | }
20 | .header image{
21 | width:180rpx;
22 | height:221rpx;
23 | /* border-radius:50%; */
24 | }
25 |
26 | /* 主体 */
27 | .main {
28 | display: flex;
29 | flex-direction: column;
30 | padding-left: 70rpx;
31 | padding-right: 70rpx;
32 | }
33 | .tips {
34 | color: #999999;
35 | font-size: 28rpx;
36 | margin-top: 64rpx;
37 | margin-left: 48rpx;
38 | }
39 |
40 | /* 其他登录方式 */
41 | .other_login{
42 | display: flex;
43 | flex-direction: row;
44 | justify-content: center;
45 | align-items: center;
46 | margin-top: 256rpx;
47 | text-align: center;
48 | }
49 | .login_icon{
50 | border: none;
51 | font-size: 64rpx;
52 | margin: 0 64rpx 0 64rpx;
53 | color: rgba(0,0,0,0.7)
54 | }
55 | .wechat_color{
56 | color: #83DC42;
57 | }
58 | .weibo_color{
59 | color: #F9221D;
60 | }
61 | .github_color{
62 | color: #24292E;
63 | }
64 |
65 | /* 底部 */
66 | .footer{
67 | display: flex;
68 | flex-direction: row;
69 | justify-content: center;
70 | align-items: center;
71 | font-size: 28rpx;
72 | margin-top: 64rpx;
73 | color: rgba(0,0,0,0.7);
74 | text-align: center;
75 | height: 40rpx;
76 | line-height: 40rpx;
77 | }
78 | .footer text{
79 | font-size: 24rpx;
80 | margin-left: 15rpx;
81 | margin-right: 15rpx;
82 | }
--------------------------------------------------------------------------------
/pages/login/forget.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 若你忘记了密码,可在此重置新密码。
8 |
9 |
10 |
11 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
108 |
109 |
113 |
--------------------------------------------------------------------------------
/pages/login/login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
96 |
97 |
122 |
--------------------------------------------------------------------------------
/pages/project/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
80 |
--------------------------------------------------------------------------------
/pages/project/project-list.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
10 |
11 |
12 | 项目名称
13 | {{item.projectName}}
14 |
15 |
16 | 项目类型
17 | {{item.projectType}}
18 |
19 |
20 | 所在区域
21 | {{item.area}}
22 |
23 |
24 | 项目地址
25 | {{item.addr}}
26 |
27 |
28 | 立项人
29 | {{item.createUser}}
30 |
31 |
32 | 审核状态
33 |
34 |
36 |
37 |
38 |
39 | 附件
40 |
41 |
42 |
43 | {{atta.name}}
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
126 |
127 |
132 |
--------------------------------------------------------------------------------
/pages/user/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{$t('BasicInfo')}}
6 |
7 |
8 |
9 |
10 | {{$t('UserName')}}
11 | {{user?user.name:''}}
12 |
13 |
14 |
15 |
16 | {{$t('Name')}}
17 | {{user?user.userName:''}}
18 |
19 |
20 |
21 |
22 | {{$t('Role')}}
23 | {{user?user.roleStr:''}}
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | {{appInfo.name}} {{appInfo.version}}
34 |
35 |
36 |
37 |
38 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
211 |
--------------------------------------------------------------------------------
/pages/user/setting.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
35 |
36 |
37 |
38 |
39 |
40 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | {{item.text}}
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
181 |
--------------------------------------------------------------------------------
/pages/yzcloud/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
85 |
--------------------------------------------------------------------------------
/pages/yzcloud/signclient.js:
--------------------------------------------------------------------------------
1 | import CryptoJS from 'crypto-js'
2 |
3 | function uniqSortParams(params) {
4 | delete params.sign
5 |
6 | var var5 = []
7 | var var6 = 0
8 | for (var key in params) {
9 | var5[var6] = key
10 | var6++
11 | }
12 | var5.sort(function(a, b) {
13 | return a.localeCompare(b, 'zh-CN')
14 | })
15 |
16 | var result = ""
17 | for (var var7 = 0; var7 < var5.length; var7++) {
18 | var key = var5[var7]
19 | var var8 = params[key]
20 | var8.sort(function(a, b) {
21 | return a.localeCompare(b, 'zh-CN')
22 | })
23 |
24 | if (var8 != null && var8.length > 0) {
25 | for (var var9 = 0; var9 < var8.length; var9++) {
26 | result += key + "=" + var8[var9]
27 | }
28 | } else {
29 | result += key + "="
30 | }
31 | }
32 | return result
33 | }
34 |
35 | function hmacSHA256(data, key) {
36 | data != null ? data : ""
37 | var var2 = CryptoJS.HmacSHA256(data, key)
38 | var var3 = var2.toString(CryptoJS.enc.Hex)
39 | return var3.toUpperCase()
40 | }
41 |
42 | export const generateSign = (secret, params) => {
43 | var fullParamStr = uniqSortParams(params)
44 | return hmacSHA256(fullParamStr, secret)
45 | }
46 |
--------------------------------------------------------------------------------
/pages/yzcloud/yz-edit.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 文档地址无效,无法加载该文档
5 |
6 |
7 |
8 |
46 |
--------------------------------------------------------------------------------
/pages/yzcloud/yz-preview-callback.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ convertMsg }}
5 | 文档地址无效,无法加载该文档
6 |
7 |
8 |
9 |
88 |
--------------------------------------------------------------------------------
/static/img/backtop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavieAll/uniapp-admin/9c866f28530db2f7d9ea71cc06864da46c8d83da/static/img/backtop.png
--------------------------------------------------------------------------------
/static/img/edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavieAll/uniapp-admin/9c866f28530db2f7d9ea71cc06864da46c8d83da/static/img/edit.png
--------------------------------------------------------------------------------
/static/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavieAll/uniapp-admin/9c866f28530db2f7d9ea71cc06864da46c8d83da/static/img/logo.png
--------------------------------------------------------------------------------
/static/img/project/asset.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavieAll/uniapp-admin/9c866f28530db2f7d9ea71cc06864da46c8d83da/static/img/project/asset.png
--------------------------------------------------------------------------------
/static/img/project/build.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavieAll/uniapp-admin/9c866f28530db2f7d9ea71cc06864da46c8d83da/static/img/project/build.png
--------------------------------------------------------------------------------
/static/img/project/land.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavieAll/uniapp-admin/9c866f28530db2f7d9ea71cc06864da46c8d83da/static/img/project/land.png
--------------------------------------------------------------------------------
/static/img/project/purchase.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavieAll/uniapp-admin/9c866f28530db2f7d9ea71cc06864da46c8d83da/static/img/project/purchase.png
--------------------------------------------------------------------------------
/static/img/tabbar/home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavieAll/uniapp-admin/9c866f28530db2f7d9ea71cc06864da46c8d83da/static/img/tabbar/home.png
--------------------------------------------------------------------------------
/static/img/tabbar/homeHL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavieAll/uniapp-admin/9c866f28530db2f7d9ea71cc06864da46c8d83da/static/img/tabbar/homeHL.png
--------------------------------------------------------------------------------
/static/img/tabbar/org.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavieAll/uniapp-admin/9c866f28530db2f7d9ea71cc06864da46c8d83da/static/img/tabbar/org.png
--------------------------------------------------------------------------------
/static/img/tabbar/orgHL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavieAll/uniapp-admin/9c866f28530db2f7d9ea71cc06864da46c8d83da/static/img/tabbar/orgHL.png
--------------------------------------------------------------------------------
/static/img/tabbar/project.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavieAll/uniapp-admin/9c866f28530db2f7d9ea71cc06864da46c8d83da/static/img/tabbar/project.png
--------------------------------------------------------------------------------
/static/img/tabbar/projectHL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavieAll/uniapp-admin/9c866f28530db2f7d9ea71cc06864da46c8d83da/static/img/tabbar/projectHL.png
--------------------------------------------------------------------------------
/static/img/tabbar/todo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavieAll/uniapp-admin/9c866f28530db2f7d9ea71cc06864da46c8d83da/static/img/tabbar/todo.png
--------------------------------------------------------------------------------
/static/img/tabbar/todoHL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavieAll/uniapp-admin/9c866f28530db2f7d9ea71cc06864da46c8d83da/static/img/tabbar/todoHL.png
--------------------------------------------------------------------------------
/static/img/tabbar/tongji.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavieAll/uniapp-admin/9c866f28530db2f7d9ea71cc06864da46c8d83da/static/img/tabbar/tongji.png
--------------------------------------------------------------------------------
/static/img/tabbar/tongjiHL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavieAll/uniapp-admin/9c866f28530db2f7d9ea71cc06864da46c8d83da/static/img/tabbar/tongjiHL.png
--------------------------------------------------------------------------------
/static/img/tabbar/user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavieAll/uniapp-admin/9c866f28530db2f7d9ea71cc06864da46c8d83da/static/img/tabbar/user.png
--------------------------------------------------------------------------------
/static/img/tabbar/userHL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavieAll/uniapp-admin/9c866f28530db2f7d9ea71cc06864da46c8d83da/static/img/tabbar/userHL.png
--------------------------------------------------------------------------------
/static/img/tabbar/yz.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavieAll/uniapp-admin/9c866f28530db2f7d9ea71cc06864da46c8d83da/static/img/tabbar/yz.png
--------------------------------------------------------------------------------
/static/img/tabbar/yz2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavieAll/uniapp-admin/9c866f28530db2f7d9ea71cc06864da46c8d83da/static/img/tabbar/yz2.png
--------------------------------------------------------------------------------
/static/img/tabbar/yzHL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavieAll/uniapp-admin/9c866f28530db2f7d9ea71cc06864da46c8d83da/static/img/tabbar/yzHL.png
--------------------------------------------------------------------------------
/static/img/tabbar/yzHL2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavieAll/uniapp-admin/9c866f28530db2f7d9ea71cc06864da46c8d83da/static/img/tabbar/yzHL2.png
--------------------------------------------------------------------------------
/static/img/user/agencyOrg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavieAll/uniapp-admin/9c866f28530db2f7d9ea71cc06864da46c8d83da/static/img/user/agencyOrg.png
--------------------------------------------------------------------------------
/static/uni.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavieAll/uniapp-admin/9c866f28530db2f7d9ea71cc06864da46c8d83da/static/uni.ttf
--------------------------------------------------------------------------------
/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 | import modules from './modules/index.js'
4 | Vue.use(Vuex)
5 |
6 | const store = new Vuex.Store(modules)
7 | export default store
8 |
--------------------------------------------------------------------------------
/store/modules/app.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import _ from 'lodash'
3 | export default {
4 | state: {
5 | themeBgColor: '',
6 | darkMode: null
7 | },
8 | getters: {
9 | themeBgColor: state => {
10 | const color = Vue.prototype.$cache.get('_themeBgColor')
11 | if (_.isEmpty(color) && _.isEmpty(state.themeBgColor)) {
12 | return '#27547d'
13 | }
14 | return !_.isEmpty(state.themeBgColor) ? state.themeBgColor : color
15 | },
16 | darkMode: state => {
17 | const dark = Vue.prototype.$cache.get('_darkMode')
18 | if (_.isNil(dark) && _.isNil(state.darkMode)) {
19 | return false
20 | }
21 | return !_.isNil(state.darkMode) ? state.darkMode : dark
22 | }
23 | },
24 | mutations: {
25 | setThemeBgColor: (state, themeBgColor) => {
26 | // state.themeBgColor = themeBgColor
27 | Vue.set(state, 'themeBgColor', themeBgColor)
28 | Vue.prototype.$cache.set('_themeBgColor', themeBgColor, 0)
29 | },
30 | setDarkMode: (state, darkMode) => {
31 | Vue.set(state, 'darkMode', darkMode)
32 | Vue.prototype.$cache.set('_darkMode', darkMode, 0)
33 | }
34 | },
35 | actions: {
36 | initThemeBgColor({ commit }, themeBgColor) {
37 | commit('setThemeBgColor', themeBgColor)
38 | },
39 | initDarkMode({ commit }, darkMode) {
40 | commit('setDarkMode', darkMode)
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/store/modules/index.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash'
2 |
3 | const files = require.context('.', false, /\.js$/)
4 | const modules = {}
5 |
6 | files.keys().forEach(key => {
7 | if (key === './index.js') return
8 | _.mergeWith(modules,files(key).default)
9 | })
10 | export default modules
11 |
--------------------------------------------------------------------------------
/store/modules/user.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import _ from 'lodash'
3 | export default {
4 | state: {
5 | user: null
6 | },
7 | mutations: {
8 | login(state, user) {
9 | state.user = user
10 | // 缓存用户信息
11 | Vue.prototype.$cache.set('_userInfo', user, 0)
12 | },
13 | logout(state) {
14 | state.user = null
15 | // 清理缓存用户信息
16 | Vue.prototype.$cache.delete('_userInfo')
17 | }
18 | },
19 | actions: {
20 | autoLogin({ commit, getters, dispatch }) {
21 | // 判断本地是否有账号信息,如果有,就自动重新登录
22 | if (getters.user && getters.user.id && getters.user.name && getters.user.passwd) {
23 | const params = {
24 | name: getters.user.name,
25 | passwd: getters.user.passwd
26 | }
27 | uni.showLoading({
28 | title: '自动登录中...'
29 | })
30 | dispatch('login', params).then(res => {
31 | uni.hideLoading()
32 | // uni.showToast({
33 | // title: '已自动登录!',
34 | // icon: 'success'
35 | // })
36 | }).catch(() => {
37 | uni.hideLoading()
38 | uni.showToast({
39 | title: '会话过期,请重新登录!',
40 | icon: 'none'
41 | })
42 | setTimeout(() => {
43 | uni.reLaunch({
44 | url: '/pages/login/login'
45 | })
46 | }, 1000)
47 | })
48 | } else {
49 | // 如果本地没有账号信息,就提示用户必须登录
50 | uni.showModal({
51 | title: '未登录',
52 | content: '您未登录,需要登录后才能继续',
53 | showCancel: false,
54 | confirmText: '确定',
55 | success: res => {
56 | if (res.confirm) {
57 | uni.reLaunch({
58 | url: '/pages/login/login'
59 | })
60 | }
61 | }
62 | })
63 | }
64 | },
65 | login({ commit }, params) {
66 | return new Promise((resolve, reject) => {
67 | Vue.prototype.$minApi.login(params).then(res => {
68 | if (res.ok()) {
69 | let tmp = { ...params, ...res.data }
70 | commit('login', tmp)
71 |
72 | // 关于消息推送的保存
73 | // 保存clientid到服务器
74 | // #ifdef APP-PLUS
75 | const clientInfo = plus.push.getClientInfo()
76 | let pushUser = {
77 | clientid: clientInfo.clientid,
78 | appid: clientInfo.appid,
79 | appkey: clientInfo.appkey,
80 | userName: tmp.userName,
81 | userRole: tmp.roleStr
82 | }
83 | // 提交api请求,服务端保存客户端角色信息
84 | // Vue.prototype.$minApi.savePushUser(pushUser)
85 | // #endif
86 |
87 | resolve(res)
88 | } else {
89 | reject(res)
90 | }
91 | }).catch(err => {
92 | reject(err)
93 | })
94 | })
95 | },
96 | logout({ commit }) {
97 | commit('logout')
98 | uni.reLaunch({
99 | url: '/pages/login/login'
100 | })
101 | }
102 | },
103 | getters: {
104 | user: state => {
105 | if (state.user) {
106 | return state.user
107 | }
108 | return Vue.prototype.$cache.get('_userInfo')
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/uni.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * 这里是uni-app内置的常用样式变量
3 | *
4 | * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
5 | * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
6 | *
7 | */
8 |
9 | /**
10 | * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
11 | *
12 | * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
13 | */
14 |
15 | /* 颜色变量 */
16 |
17 | /* 行为相关颜色 */
18 | $uni-color-primary: #007aff;
19 | $uni-color-success: #4cd964;
20 | $uni-color-warning: #f0ad4e;
21 | $uni-color-error: #dd524d;
22 |
23 | /* 文字基本颜色 */
24 | $uni-text-color:#333;//基本色
25 | $uni-text-color-inverse:#fff;//反色
26 | $uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
27 | $uni-text-color-placeholder: #808080;
28 | $uni-text-color-disable:#c0c0c0;
29 |
30 | /* 背景颜色 */
31 | $uni-bg-color:#ffffff;
32 | $uni-bg-color-grey:#f8f8f8;
33 | $uni-bg-color-hover:#f1f1f1;//点击状态颜色
34 | $uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
35 |
36 | /* 边框颜色 */
37 | $uni-border-color:#c8c7cc;
38 |
39 | /* 尺寸变量 */
40 |
41 | /* 文字尺寸 */
42 | $uni-font-size-sm:24upx;
43 | $uni-font-size-base:28upx;
44 | $uni-font-size-lg:32upx;
45 |
46 | /* 图片尺寸 */
47 | $uni-img-size-sm:40upx;
48 | $uni-img-size-base:52upx;
49 | $uni-img-size-lg:80upx;
50 |
51 | /* Border Radius */
52 | $uni-border-radius-sm: 4upx;
53 | $uni-border-radius-base: 6upx;
54 | $uni-border-radius-lg: 12upx;
55 | $uni-border-radius-circle: 50%;
56 |
57 | /* 水平间距 */
58 | $uni-spacing-row-sm: 10px;
59 | $uni-spacing-row-base: 20upx;
60 | $uni-spacing-row-lg: 30upx;
61 |
62 | /* 垂直间距 */
63 | $uni-spacing-col-sm: 8upx;
64 | $uni-spacing-col-base: 16upx;
65 | $uni-spacing-col-lg: 24upx;
66 |
67 | /* 透明度 */
68 | $uni-opacity-disabled: 0.3; // 组件禁用态的透明度
69 |
70 | /* 文章场景相关 */
71 | $uni-color-title: #2C405A; // 文章标题颜色
72 | $uni-font-size-title:40upx;
73 | $uni-color-subtitle: #555555; // 二级标题颜色
74 | $uni-font-size-subtitle:36upx;
75 | $uni-color-paragraph: #3F536E; // 文章段落颜色
76 | $uni-font-size-paragraph:30upx;
--------------------------------------------------------------------------------
/unipush离线集成指南.md:
--------------------------------------------------------------------------------
1 | ## 关于unipush离线集成的方法
2 | ### 准备
3 | 1. 通过官方途径申请UniPush账号,参考[使用指南](http://ask.dcloud.net.cn/article/35622)
4 |
5 | 2. [离线SDK](https://ask.dcloud.net.cn/article/103)
6 |
7 | ### 配置
8 |
9 | * 依赖文件
10 |
11 | 将aps-unipush-release.aar拷贝到已有项目libs文件夹下。
12 |
13 | * gradle配置
14 |
15 | 打开build.gradle,在defaultConfig添加manifestPlaceholders节点,如下图所示,将io.dcloud.HBuilder替换成自己的应用包名,将appid等信息替换成申请之后的appid等。
16 | ~~~
17 | android {
18 | defaultConfig {
19 | manifestPlaceholders = [
20 | "plus.unipush.appid" : "pPyZWvH3Fa6PXba19ID0091",
21 | "plus.unipush.appkey" : "b7dOGlNPHR7pqwUxcXPVi44",
22 | "plus.unipush.appsecret": "IxVYAT9qws8dlNElacmSg12",
23 | "apk.applicationId":"io.dcloud.HBuilder"
24 | ]
25 | }
26 | }
27 | ~~~
28 |
29 | * 厂商配置
30 |
31 | 添加下列内容到androidmanifest.xml中(未申请平台无需添加)
32 | ~~~
33 |
36 |
39 |
42 |
45 |
48 |
51 |
54 |
57 |
60 | ~~~
61 |
62 | 修改build.gradle,添加对应平台申请的appkey或appid(键名必须统一,如XIAOMI_APP_ID比如同时存在于build.gradle文件和Androidmanifest.xml文件中),如下所示:
63 |
64 | ~~~
65 | android {
66 | defaultConfig {
67 | manifestPlaceholders = [
68 | "plus.unipush.appid" : "pPyZWvH3Fa6PXba19ID0091",
69 | "plus.unipush.appkey" : "b7dOGlNPHR7pqwUxcXPVi45",
70 | "plus.unipush.appsecret": "IxVYAT9qws8dlNElacmSg12",
71 | "apk.applicationId":"io.dcloud.HBuilder",
72 | "XIAOMI_APP_ID":"ccccccccc"
73 | ]
74 | }
75 | }
76 | ~~~
77 |
78 | * dcloud_properties.xml配置
79 |
80 | 在properties中添加如下配置,features节点与services节点必须同时配置!
81 |
82 | ~~~
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | ~~~
94 |
95 | * 其余配置
96 |
97 | oppo集成UniPush时需在Androidmanifest.xml的入口activity中添加如下配置:
98 |
99 | ~~~
100 |
102 |
103 |
104 |
105 |
106 | /*oppo配置开始*/
107 |
108 |
109 |
110 |
111 | /*oppo配置结束*/
112 |
113 | ~~~
114 |
115 | ### 注意
116 |
117 | * UniPush与个推及小米推送存在冲突,使用时请确保小米推送及个推相关文件已删除。
118 | * 上述账号信息仅为展示使用,使用时需替换成自己申请的appkey等信息。
119 | * 为了最大化的减少配置,权限及其他厂商配置统统放入aar中。
--------------------------------------------------------------------------------
/utils/MinCache.js:
--------------------------------------------------------------------------------
1 | let cacheMap = new Map()
2 | let timeoutDefault = 1200
3 |
4 | function isTimeout(name) {
5 | const data = cacheMap.get(name)
6 | if (!data) return true
7 | if (data.timeout === 0) return false
8 | const currentTime = Date.now()
9 | const overTime = (currentTime - data.createTime) / 1000
10 | if (overTime > data.timeout) {
11 | cacheMap.delete(name)
12 | if (name.startsWith('_')) {
13 | try {
14 | uni.removeStorageSync(name)
15 | } catch (e) {
16 | console.log(e)
17 | }
18 | }
19 | return true
20 | }
21 | return false
22 | }
23 |
24 | class CacheCell {
25 | constructor(data, timeout) {
26 | this.data = data
27 | this.timeout = timeout
28 | this.createTime = Date.now()
29 | }
30 | }
31 |
32 | class MinCache {
33 | constructor(timeout) {
34 | try {
35 | const res = uni.getStorageInfoSync()
36 | res.keys.forEach(name => {
37 | try {
38 | const value = uni.getStorageSync(name)
39 | cacheMap.set(name, value)
40 | } catch (e) {
41 | console.log(e)
42 | }
43 | })
44 | } catch (e) {
45 | console.log(e)
46 | }
47 | timeoutDefault = timeout
48 | }
49 | set(name, data, timeout = timeoutDefault) {
50 | const cachecell = new CacheCell(data, timeout)
51 | let cache = null
52 | if (name.startsWith('_')) {
53 | try {
54 | uni.setStorageSync(name, cachecell)
55 | cache = cacheMap.set(name, cachecell)
56 | } catch (e) {
57 | console.log(e)
58 | }
59 | } else {
60 | cache = cacheMap.set(name, cachecell)
61 | }
62 | return cache
63 | }
64 | get(name) {
65 | return isTimeout(name) ? null : cacheMap.get(name).data
66 | }
67 | delete(name) {
68 | let value = false
69 | if (name.startsWith('_')) {
70 | try {
71 | uni.removeStorageSync(name)
72 | value = cacheMap.delete(name)
73 | } catch (e) {
74 | console.log(e)
75 | }
76 | } else {
77 | value = cacheMap.delete(name)
78 | }
79 | return value
80 | }
81 | has(name) {
82 | return !isTimeout(name)
83 | }
84 | clear() {
85 | let value = false
86 | try {
87 | uni.clearStorageSync()
88 | cacheMap.clear()
89 | value = true
90 | } catch (e) {
91 | console.log(e)
92 | }
93 | return value
94 | }
95 | }
96 |
97 | MinCache.install = function(Vue, {
98 | timeout = 1200
99 | } = {}) {
100 | Vue.prototype.$cache = new MinCache(timeout)
101 | }
102 |
103 | export default MinCache
104 |
--------------------------------------------------------------------------------
/utils/MinRequest.js:
--------------------------------------------------------------------------------
1 | const config = Symbol('config')
2 | const isCompleteURL = Symbol('isCompleteURL')
3 | const requestBefore = Symbol('requestBefore')
4 | const requestAfter = Symbol('requestAfter')
5 | import { checkLogin, checkResult } from '@/utils/checkResponse'
6 |
7 | class MinRequest {
8 | [config] = {
9 | baseURL: '',
10 | header: {
11 | 'content-type': 'application/json'
12 | },
13 | method: 'GET',
14 | dataType: 'json',
15 | responseType: 'text'
16 | }
17 |
18 | interceptors = {
19 | request: (func) => {
20 | if (func) {
21 | MinRequest[requestBefore] = func
22 | } else {
23 | MinRequest[requestBefore] = (request) => request
24 | }
25 |
26 | },
27 | response: (func) => {
28 | if (func) {
29 | MinRequest[requestAfter] = func
30 | } else {
31 | MinRequest[requestAfter] = (response) => response
32 | }
33 | }
34 | }
35 |
36 | static[requestBefore](config) {
37 | return config
38 | }
39 |
40 | static[requestAfter](response) {
41 | return response
42 | }
43 |
44 | static[isCompleteURL](url) {
45 | return /(http|https):\/\/([\w.]+\/?)\S*/.test(url)
46 | }
47 |
48 | setConfig(func) {
49 | this[config] = func(this[config])
50 | }
51 |
52 | request(options = {}) {
53 | options.baseURL = options.baseURL || this[config].baseURL
54 | options.dataType = options.dataType || this[config].dataType
55 | options.url = MinRequest[isCompleteURL](options.url) ? options.url : (options.baseURL + options.url)
56 | options.data = options.data
57 | options.header = { ...options.header,
58 | ...this[config].header
59 | }
60 | options.method = options.method || this[config].method
61 |
62 | options = { ...options,
63 | ...MinRequest[requestBefore](options)
64 | }
65 |
66 | return new Promise((resolve, reject) => {
67 | options.success = function(res) {
68 | resolve(MinRequest[requestAfter](res))
69 | }
70 | options.fail = function(err) {
71 | reject(MinRequest[requestAfter](err))
72 | }
73 | uni.request(options)
74 | })
75 | }
76 |
77 | get(url, data, options = {}) {
78 | options.url = url
79 | options.data = data
80 | options.method = 'GET'
81 | return this.request(options).then(checkLogin).then(checkResult)
82 | }
83 |
84 | post(url, data, options = {}) {
85 | options.url = url
86 | options.data = data
87 | options.method = 'POST'
88 | return this.request(options).then(checkLogin).then(checkResult)
89 | }
90 |
91 | delete(url, data, options = {}) {
92 | options.url = url
93 | options.data = data
94 | options.method = 'DELETE'
95 | return this.request(options).then(checkLogin).then(checkResult)
96 | }
97 | }
98 |
99 | MinRequest.install = function(Vue) {
100 | Vue.mixin({
101 | beforeCreate: function() {
102 | if (this.$options.minApi) {
103 | Vue._minApi = this.$options.minApi
104 | }
105 | }
106 | })
107 | Object.defineProperty(Vue.prototype, '$minApi', {
108 | get: function() {
109 | return Vue._minApi.apis
110 | }
111 | })
112 | }
113 |
114 | export default MinRequest
115 |
--------------------------------------------------------------------------------
/utils/MinRouter.js:
--------------------------------------------------------------------------------
1 | const toString = Object.prototype.toString
2 |
3 | function isObject(value) {
4 | return toString.call(value) === '[object Object]'
5 | }
6 |
7 | function isString(value) {
8 | return toString.call(value) === '[object String]'
9 | }
10 |
11 | function isDefault(value) {
12 | return value === void 0
13 | }
14 |
15 | function openPage(args) {
16 | let name, query = {},
17 | queryStr = null,
18 | path, type, isName = false
19 | switch (true) {
20 | case isObject(args):
21 | ({
22 | name,
23 | query = {}
24 | } = args)
25 | break
26 | case isString(args):
27 | name = args
28 | break
29 | default:
30 | throw new Error('参数必须是对象或者字符串')
31 | }
32 | if (isObject(query)) {
33 | queryStr = encodeURIComponent(JSON.stringify(query))
34 | } else {
35 | throw new Error('query数据必须是Object')
36 | }
37 | this.$minRouter.forEach(item => {
38 | if (item.name === name) {
39 | path = item.path
40 | type = item.type || 'navigateTo'
41 | isName = true
42 | }
43 | })
44 | if (!isName) {
45 | throw new Error(`没有${name}页面`)
46 | }
47 | if (!['navigateTo', 'switchTab', 'reLaunch', 'redirectTo'].includes(type)) {
48 | throw new Error(`name:${name}里面的type必须是以下的值['navigateTo', 'switchTab', 'reLaunch', 'redirectTo']`)
49 | }
50 | return new Promise((resolve, reject) => {
51 | uni[type]({
52 | url: `/${path}?query=${queryStr}`,
53 | success: resolve,
54 | fail: reject
55 | })
56 | })
57 | }
58 |
59 | function parseURL() {
60 | const query = this.$root.$mp.query.query
61 | if (query) {
62 | return JSON.parse(decodeURIComponent(query))
63 | } else {
64 | return {}
65 | }
66 | }
67 |
68 | function install(Vue) {
69 | Vue.mixin({
70 | beforeCreate: function() {
71 | if (!isDefault(this.$options.minRouter)) {
72 | Vue._minRouter = this.$options.minRouter
73 | }
74 | }
75 | })
76 | Object.defineProperty(Vue.prototype, '$minRouter', {
77 | get: function() {
78 | return Vue._minRouter._router
79 | }
80 | })
81 | Object.defineProperty(Vue.prototype, '$parseURL', {
82 | get: function() {
83 | return Vue._minRouter.parseURL
84 | }
85 | })
86 | Object.defineProperty(Vue.prototype, '$openPage', {
87 | get: function() {
88 | return Vue._minRouter.openPage
89 | }
90 | })
91 | }
92 |
93 | function MinRouter(options) {
94 | if (!(this instanceof MinRouter)) {
95 | throw Error("MinRouter是一个构造函数,应该用`new`关键字调用")
96 | }
97 | isDefault(options) && (options = {})
98 | this.options = options
99 | this._router = options.routes || []
100 | }
101 | MinRouter.install = install
102 | MinRouter.prototype.openPage = openPage
103 | MinRouter.prototype.parseURL = parseURL
104 | export default MinRouter
105 |
--------------------------------------------------------------------------------
/utils/checkResponse.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | const HttpErrorCode = {
3 | 400: '请求错误',
4 | 401: '未授权,请登陆',
5 | 403: '拒绝访问',
6 | 404: '请求地址出错',
7 | 408: '请求超时',
8 | 500: '服务器内部错误',
9 | 501: '服务未实现',
10 | 502: '网关错误',
11 | 503: '服务不可用',
12 | 504: '网关超时',
13 | 505: 'HTTP版本不受支持'
14 | }
15 |
16 | // 去抖
17 | function debounce(fn, wait) {
18 | let callback = fn
19 | let timerId = null
20 |
21 | function debounced() {
22 | // 保存作用域
23 | let context = this
24 | // 保存参数,例如 event 对象
25 | let args = arguments
26 |
27 | clearTimeout(timerId)
28 | timerId = setTimeout(function() {
29 | callback.apply(context, args)
30 | }, wait)
31 | }
32 |
33 | // 返回一个闭包
34 | return debounced
35 | }
36 |
37 | // 节流
38 | function throttle(fn, wait) {
39 | let callback = fn
40 | let timerId = null
41 |
42 | // 是否是第一次执行
43 | let firstInvoke = true
44 |
45 | function throttled() {
46 | let context = this
47 | let args = arguments
48 |
49 | // 如果是第一次触发,直接执行
50 | if (firstInvoke) {
51 | callback.apply(context, args)
52 | firstInvoke = false
53 | return
54 | }
55 |
56 | // 如果定时器已存在,直接返回。
57 | if (timerId) {
58 | return
59 | }
60 |
61 | timerId = setTimeout(function() {
62 | // 注意这里 将 clearTimeout 放到 内部来执行了
63 | clearTimeout(timerId)
64 | timerId = null
65 |
66 | callback.apply(context, args)
67 | }, wait)
68 | }
69 |
70 | // 返回一个闭包
71 | return throttled
72 | }
73 |
74 | /**
75 | * 跳转到登录页面
76 | */
77 | const goLoginDebounce = debounce(
78 | function() {
79 | Vue.prototype.$store.dispatch('logout')
80 | },
81 | 250
82 | )
83 | /**
84 | * 展示出错误信息
85 | */
86 | const showErrorMsg = debounce(
87 | function(msg) {
88 | uni.showToast({
89 | title: msg,
90 | icon: 'none'
91 | })
92 | },
93 | 250
94 | )
95 |
96 | /**
97 | *
98 | *检查reponse返回状态
99 | * @author silianpan
100 | * @date 2019-11-29
101 | * @param {*} [response={}]
102 | * @returns
103 | */
104 | export const checkStatus = (response = {}) => {
105 | let {
106 | status,
107 | statusText
108 | } = response
109 | if (status === 200 || status === 304) {
110 | return response.data
111 | } else {
112 | let msg = HttpErrorCode[status] || statusText
113 | showErrorMsg(msg)
114 | return {
115 | code: status,
116 | msg: msg
117 | }
118 | }
119 | }
120 |
121 | /**
122 | *检查登录状态
123 | *
124 | * @author silianpan
125 | * @date 2019-11-29
126 | * @param {*} response
127 | * @returns
128 | */
129 | export const checkLogin = (response) => {
130 | let { code } = response
131 | if (code === 401) {
132 | goLoginDebounce()
133 | }
134 | return response
135 | }
136 | /**
137 | *检查服务器返回结果
138 | * @author silianpan
139 | * @date 2019-11-29
140 | * @param {*} result
141 | * @returns
142 | */
143 | export const checkResult = (result) => {
144 | // 对后台返回结果检测
145 | // if (!result.isOk) {
146 | // showErrorMsg(result.errMsg || '请求失败')
147 | // }
148 | let { code } = result
149 | result.ok = function() {
150 | return false
151 | }
152 | if (code === 200) {
153 | result.ok = function() {
154 | return true
155 | }
156 | } else {
157 | showErrorMsg(result.msg || HttpErrorCode[code] || '请求失败')
158 | }
159 | return result
160 | }
161 |
--------------------------------------------------------------------------------
/utils/datetime.js:
--------------------------------------------------------------------------------
1 | export const formatDateDay = time => {
2 | return parseTime(time, '{y}-{m}-{d}')
3 | }
4 | export const formatDate = time => {
5 | return parseTime(time, '{y}-{m}-{d} {h}:{i}')
6 | }
7 | const parseTime = (time, cFormat) => {
8 | if (!time) {
9 | return null
10 | }
11 | if (arguments.length === 0) {
12 | return null
13 | }
14 | const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
15 | let date
16 | if (typeof time === 'object') {
17 | date = time
18 | } else {
19 | if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
20 | time = parseInt(time)
21 | }
22 | if ((typeof time === 'number') && (time.toString().length === 10)) {
23 | time = time * 1000
24 | }
25 | date = new Date(time)
26 | }
27 | const formatObj = {
28 | y: date.getFullYear(),
29 | m: date.getMonth() + 1,
30 | d: date.getDate(),
31 | h: date.getHours(),
32 | i: date.getMinutes(),
33 | s: date.getSeconds(),
34 | a: date.getDay()
35 | }
36 | const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
37 | let value = formatObj[key]
38 | // Note: getDay() returns 0 on Sunday
39 | if (key === 'a') {
40 | return ['日', '一', '二', '三', '四', '五', '六'][value]
41 | }
42 | if (result.length > 0 && value < 10) {
43 | value = '0' + value
44 | }
45 | return value || 0
46 | })
47 | return time_str
48 | }
49 | export const formatSimpleTime = (time, option) => {
50 | if (('' + time).length === 10) {
51 | time = parseInt(time) * 1000
52 | } else {
53 | time = +time
54 | }
55 | const d = new Date(time)
56 | const now = Date.now()
57 |
58 | const diff = (now - d) / 1000
59 |
60 | if (diff < 30) {
61 | return '刚刚'
62 | } else if (diff < 3600) {
63 | // less 1 hour
64 | return Math.ceil(diff / 60) + '分钟前'
65 | } else if (diff < 3600 * 24) {
66 | return Math.ceil(diff / 3600) + '小时前'
67 | } else if (diff < 3600 * 24 * 2) {
68 | return '1天前'
69 | }
70 | if (option) {
71 | return parseTime(time, option)
72 | } else {
73 | return (
74 | d.getMonth() +
75 | 1 +
76 | '月' +
77 | d.getDate() +
78 | '日' +
79 | d.getHours() +
80 | '时' +
81 | d.getMinutes() +
82 | '分'
83 | )
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/utils/graceChecker.js:
--------------------------------------------------------------------------------
1 | /**
2 | 数据验证(表单验证)
3 | 来自 grace.hcoder.net
4 | 作者 hcoder 深海
5 | */
6 | module.exports = {
7 | error:'',
8 | check : function (data, rule){
9 | for(var i = 0; i < rule.length; i++){
10 | if (!rule[i].checkType){return true;}
11 | if (!rule[i].name) {return true;}
12 | if (!rule[i].errorMsg) {return true;}
13 | if (!data[rule[i].name]) {this.error = rule[i].errorMsg; return false;}
14 | switch (rule[i].checkType){
15 | case 'string':
16 | var reg = new RegExp('^.{' + rule[i].checkRule + '}$');
17 | if(!reg.test(data[rule[i].name])) {this.error = rule[i].errorMsg; return false;}
18 | break;
19 | case 'int':
20 | var reg = new RegExp('^(-[1-9]|[1-9])[0-9]{' + rule[i].checkRule + '}$');
21 | if(!reg.test(data[rule[i].name])) {this.error = rule[i].errorMsg; return false;}
22 | break;
23 | break;
24 | case 'between':
25 | if (!this.isNumber(data[rule[i].name])){
26 | this.error = rule[i].errorMsg;
27 | return false;
28 | }
29 | var minMax = rule[i].checkRule.split(',');
30 | minMax[0] = Number(minMax[0]);
31 | minMax[1] = Number(minMax[1]);
32 | if (data[rule[i].name] > minMax[1] || data[rule[i].name] < minMax[0]) {
33 | this.error = rule[i].errorMsg;
34 | return false;
35 | }
36 | break;
37 | case 'betweenD':
38 | var reg = /^-?[1-9][0-9]?$/;
39 | if (!reg.test(data[rule[i].name])) { this.error = rule[i].errorMsg; return false; }
40 | var minMax = rule[i].checkRule.split(',');
41 | minMax[0] = Number(minMax[0]);
42 | minMax[1] = Number(minMax[1]);
43 | if (data[rule[i].name] > minMax[1] || data[rule[i].name] < minMax[0]) {
44 | this.error = rule[i].errorMsg;
45 | return false;
46 | }
47 | break;
48 | case 'betweenF':
49 | var reg = /^-?[0-9][0-9]?.+[0-9]+$/;
50 | if (!reg.test(data[rule[i].name])){this.error = rule[i].errorMsg; return false;}
51 | var minMax = rule[i].checkRule.split(',');
52 | minMax[0] = Number(minMax[0]);
53 | minMax[1] = Number(minMax[1]);
54 | if (data[rule[i].name] > minMax[1] || data[rule[i].name] < minMax[0]) {
55 | this.error = rule[i].errorMsg;
56 | return false;
57 | }
58 | break;
59 | case 'same':
60 | if (data[rule[i].name] != rule[i].checkRule) { this.error = rule[i].errorMsg; return false;}
61 | break;
62 | case 'notsame':
63 | if (data[rule[i].name] == rule[i].checkRule) { this.error = rule[i].errorMsg; return false; }
64 | break;
65 | case 'email':
66 | var reg = /^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
67 | if (!reg.test(data[rule[i].name])) { this.error = rule[i].errorMsg; return false; }
68 | break;
69 | case 'phoneno':
70 | var reg = /^1[0-9]{10,10}$/;
71 | if (!reg.test(data[rule[i].name])) { this.error = rule[i].errorMsg; return false; }
72 | break;
73 | case 'zipcode':
74 | var reg = /^[0-9]{6}$/;
75 | if (!reg.test(data[rule[i].name])) { this.error = rule[i].errorMsg; return false; }
76 | break;
77 | case 'reg':
78 | var reg = new RegExp(rule[i].checkRule);
79 | if (!reg.test(data[rule[i].name])) { this.error = rule[i].errorMsg; return false; }
80 | break;
81 | case 'in':
82 | if(rule[i].checkRule.indexOf(data[rule[i].name]) == -1){
83 | this.error = rule[i].errorMsg; return false;
84 | }
85 | break;
86 | case 'notnull':
87 | if(data[rule[i].name] == null || data[rule[i].name].length < 1){this.error = rule[i].errorMsg; return false;}
88 | break;
89 | }
90 | }
91 | return true;
92 | },
93 | isNumber : function (checkVal){
94 | var reg = /^-?[1-9][0-9]?.?[0-9]*$/;
95 | return reg.test(checkVal);
96 | }
97 | }
--------------------------------------------------------------------------------
/utils/index.js:
--------------------------------------------------------------------------------
1 | import globalConfig from '@/config'
2 |
3 | /**
4 | * 随机从数据中选取n项元素
5 | */
6 | export const getRandomArrayElements = (arr, count) => {
7 | var shuffled = arr.slice(0),
8 | i = arr.length,
9 | min = i - count,
10 | temp, index
11 | while (i-- > min) {
12 | index = Math.floor((i + 1) * Math.random())
13 | temp = shuffled[index]
14 | shuffled[index] = shuffled[i]
15 | shuffled[i] = temp
16 | }
17 | return shuffled.slice(min)
18 | }
19 |
20 | export const formatArr = (segmentType, sep = '/') => {
21 | let result = ''
22 | if (segmentType) {
23 | try {
24 | const segmentArr = JSON.parse(segmentType)
25 | segmentArr.forEach(item => {
26 | result += item + sep
27 | })
28 | } catch (error) {
29 | return segmentType
30 | }
31 | }
32 | if (result) {
33 | let len = sep.length > 1 ? 2 : 1
34 | result = result.substring(0, result.length - len)
35 | }
36 | return result
37 | }
38 | export const formatProjectType = pt => {
39 | if (!pt) {
40 | return ''
41 | }
42 | if (pt.indexOf('工程施工') !== -1) {
43 | return 'build.png'
44 | } else if (pt.indexOf('采购') !== -1) {
45 | return 'purchase.png'
46 | } else if (pt.indexOf('软件研发') !== -1) {
47 | return 'asset.png'
48 | } else if (pt.indexOf('系统集成') !== -1) {
49 | return 'land.png'
50 | }
51 | return ''
52 | }
53 | // 格式化附件json字符串
54 | export const formatJsonStr = attaJsonStr => {
55 | if (attaJsonStr && attaJsonStr.length > 0) {
56 | try {
57 | return JSON.parse(attaJsonStr)
58 | } catch (e) {
59 | return []
60 | }
61 | }
62 | return []
63 | }
64 |
65 | export const formatAuditStatus = status => {
66 | let text = ''
67 | let color = ''
68 | switch (status) {
69 | case '-1':
70 | ;
71 | (() => {
72 | text = '未提交'
73 | color = 'primary'
74 | })()
75 | break
76 | case '0':
77 | ;
78 | (() => {
79 | text = '不通过'
80 | color = 'error'
81 | })()
82 | break
83 | case '1':
84 | ;
85 | (() => {
86 | text = '通过'
87 | color = 'success'
88 | })()
89 | break
90 | default:
91 | ;
92 | (() => {
93 | text = '待审核'
94 | color = 'warning'
95 | })()
96 | break
97 | }
98 | return {
99 | color,
100 | text
101 | }
102 | }
103 |
104 | /**
105 | * 附件预览
106 | * @param {Object} atta附件{name:'',url:''}
107 | */
108 | export const filePreview = atta => {
109 | let name = atta.name
110 | let url = atta.url
111 | // 判断文件类型
112 | if (url && typeof url === 'string') {
113 | let index = url.lastIndexOf('.')
114 | let suffix = url.substring(index + 1)
115 | if (suffix) {
116 | suffix = suffix.toLowerCase()
117 | switch (suffix) {
118 | case 'pdf':
119 | case 'doc':
120 | case 'docx':
121 | case 'xls':
122 | case 'xlsx':
123 | case 'ppt':
124 | case 'pptx':
125 | // 转换为pdf进行预览
126 | uni.navigateTo({
127 | url: '/pages/file/file-preview?name=' + name + '&url=' + url
128 | })
129 | break
130 | case 'jpg':
131 | case 'jpeg':
132 | case 'png':
133 | case 'bmp':
134 | case 'gif':
135 | // 预览图片
136 | uni.previewImage({
137 | urls: [globalConfig.fileAddr + url],
138 | longPressActions: {
139 | itemList: ['发送给朋友', '保存图片', '收藏'],
140 | success: function(data) {
141 | console.log('选中了第' + (data.tapIndex + 1) + '个按钮,第' + (data.index + 1) + '张图片');
142 | },
143 | fail: function(err) {
144 | console.log(err.errMsg);
145 | }
146 | }
147 | })
148 | break
149 | }
150 | }
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/utils/url.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 获取href页面参数key
3 | */
4 | export const getQueryString = key => {
5 | // 方法一:正则
6 | var reg = new RegExp('(^|&)' + key + '=([^&]*)(&|$)')
7 | var r = window.location.search.substr(1).match(reg)
8 | if (r != null) return unescape(r[2])
9 | return null
10 |
11 | // 方法二:字符串
12 | // var url = window.location.search;
13 | // var theRequest = new Object();
14 | // if (url.indexOf("?") != -1) {
15 | // var str = url.substr(1);
16 | // var strs = str.split("&");
17 | // for (var i = 0; i < strs.length; i++) {
18 | // theRequest[strs[i].split("=")[0]] = (strs[i].split("=")[1]);
19 | // }
20 | // }
21 | }
22 |
23 | /**
24 | * 获取url中的参数key
25 | */
26 | export const getQueryString4Url = (url, key) => {
27 | const reg = new RegExp('(^|&)' + key + '=([^&]*)(&|$)')
28 | const r = url.substr(url.indexOf('?') + 1).match(reg)
29 | if (r != null) return unescape(r[2])
30 | return null
31 | }
32 |
--------------------------------------------------------------------------------
/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | crypto-js@^4.0.0:
6 | version "4.0.0"
7 | resolved "https://registry.npm.taobao.org/crypto-js/download/crypto-js-4.0.0.tgz#2904ab2677a9d042856a2ea2ef80de92e4a36dcc"
8 | integrity sha1-KQSrJnep0EKFai6i74DekuSjbcw=
9 |
10 | lodash@^4.17.21:
11 | version "4.17.21"
12 | resolved "https://registry.npm.taobao.org/lodash/download/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
13 | integrity sha1-Z5WRxWTDv/quhFTPCz3zcMPWkRw=
14 |
15 | vue-i18n@^8.23.0:
16 | version "8.23.0"
17 | resolved "https://registry.npm.taobao.org/vue-i18n/download/vue-i18n-8.23.0.tgz#4a65681a1dfe716d47e1d00ddbd6e0b88ea36735"
18 | integrity sha1-SmVoGh3+cW1H4dAN29bguI6jZzU=
19 |
--------------------------------------------------------------------------------