From 69e6ed8c597fe348d5206f89c6849483d0e4b699 Mon Sep 17 00:00:00 2001
From: b2894lxlx <517289602@qq.com>
Date: Tue, 29 Sep 2026 15:17:13 +0800
Subject: [PATCH] 20260929
---
.gitignore | 3 +
.idea/.gitignore | 3 +
.idea/UniappTool.xml | 17 +
.idea/inspectionProfiles/Project_Default.xml | 125 +
.idea/modules.xml | 8 +
.idea/vcs.xml | 6 +
.idea/yaodongxiao.iml | 12 +
App.vue | 127 +
api/address.js | 5 +
api/bonus.js | 7 +
api/cart.js | 5 +
api/common.js | 55 +
api/coupon.js | 7 +
api/goods.js | 19 +
api/product.js | 11 +
api/reimburse.js | 11 +
api/request.js | 106 +
api/self-report.js | 19 +
api/share.js | 27 +
api/shop.js | 5 +
api/task.js | 25 +
api/user.js | 34 +
api/visit.js | 5 +
components/AppNavbar.vue | 122 +
components/AreaPicker.vue | 250 ++
components/HeaderItem.vue | 41 +
components/Login.vue | 222 +
components/TitleItem.vue | 18 +
components/customTabbar.vue | 120 +
config/color.js | 18 +
config/index.js | 23 +
index.html | 20 +
.../luch-request/adapters/index.js | 100 +
.../luch-request/core/InterceptorManager.js | 51 +
.../luch-request/luch-request/core/Request.js | 197 +
.../luch-request/core/buildFullPath.js | 20 +
.../luch-request/core/defaults.js | 31 +
.../luch-request/core/dispatchRequest.js | 6 +
.../luch-request/core/mergeConfig.js | 103 +
.../luch-request/luch-request/core/settle.js | 16 +
.../luch-request/helpers/buildURL.js | 64 +
.../luch-request/helpers/combineURLs.js | 14 +
.../luch-request/helpers/isAbsoluteURL.js | 14 +
js_sdk/luch-request/luch-request/index.d.ts | 120 +
js_sdk/luch-request/luch-request/index.js | 2 +
js_sdk/luch-request/luch-request/utils.js | 135 +
.../luch-request/luch-request/utils/clone.js | 264 ++
js_sdk/luch-request/package.json | 19 +
js_sdk/qqmap-wx-jssdk/qqmap-wx-jssdk.js | 1122 ++++++
js_sdk/qqmap-wx-jssdk/qqmap-wx-jssdk.min.js | 1 +
main.js | 23 +
manifest.json | 78 +
package-lock.json | 96 +
package.json | 17 +
packageUser/address/index.vue | 539 +++
packageUser/chronic/index.vue | 1394 +++++++
packageUser/chronic/manage.vue | 1074 +++++
packageUser/coupon/index.vue | 299 ++
packageUser/points/index.vue | 345 ++
packageUser/self-report/create.vue | 2080 ++++++++++
packageUser/self-report/records.vue | 833 ++++
packageUser/travel/index.vue | 893 +++++
pages.json | 271 ++
pages/audit/components/AuditMovementItem.vue | 344 ++
.../audit/components/AuditSelfReportItem.vue | 1413 +++++++
pages/audit/components/AuditShareItem.vue | 1930 +++++++++
.../audit/components/AuditStoreVisitItem.vue | 348 ++
pages/audit/components/record.scss | 220 +
pages/audit/index.vue | 1387 +++++++
pages/auth/login.vue | 255 ++
pages/bonus/index.vue | 328 ++
pages/bonus/withdraw-record.vue | 254 ++
pages/bonus/withdraw.vue | 369 ++
pages/cart/index.vue | 339 ++
pages/certification/index.vue | 276 ++
pages/exchange/index.vue | 623 +++
.../components/Progress-Bar/Progress-Bar.css | 369 ++
.../components/Progress-Bar/Progress-Bar.vue | 57 +
pages/index/index.vue | 1400 +++++++
pages/mine/index.vue | 984 +++++
pages/mine/profile.vue | 261 ++
pages/order/checkout.vue | 768 ++++
pages/order/detail.vue | 256 ++
pages/order/list.vue | 240 ++
pages/performance/index.vue | 996 +++++
pages/product/detail.vue | 289 ++
pages/share/components/ShareCreatePopup.vue | 942 +++++
pages/share/create.vue | 1766 ++++++++
pages/share/detail.vue | 329 ++
pages/share/index.vue | 1639 ++++++++
pages/share/mine.vue | 727 ++++
pages/task/detail.vue | 3570 +++++++++++++++++
pages/visit/index.vue | 748 ++++
static/images/addCart.png | Bin 0 -> 6601 bytes
static/images/blob-scene-haikei.png | Bin 0 -> 2786 bytes
static/images/coupon.png | Bin 0 -> 5970 bytes
static/logo.png | Bin 0 -> 4023 bytes
static/style/common.scss | 829 ++++
static/tabbar/group-active.png | Bin 0 -> 12419 bytes
static/tabbar/group.png | Bin 0 -> 21255 bytes
static/tabbar/home-active.png | Bin 0 -> 6718 bytes
static/tabbar/home.png | Bin 0 -> 6631 bytes
static/tabbar/share-active.png | Bin 0 -> 7297 bytes
static/tabbar/share.png | Bin 0 -> 15787 bytes
static/tabbar/switch-active.png | Bin 0 -> 9763 bytes
static/tabbar/switch.png | Bin 0 -> 15614 bytes
static/tabbar/user-active.png | Bin 0 -> 7553 bytes
static/tabbar/user.png | Bin 0 -> 14740 bytes
uni.promisify.adaptor.js | 13 +
uni.scss | 77 +
util/index.js | 292 ++
util/order.js | 100 +
util/user.js | 129 +
113 files changed, 36564 insertions(+)
create mode 100644 .gitignore
create mode 100644 .idea/.gitignore
create mode 100644 .idea/UniappTool.xml
create mode 100644 .idea/inspectionProfiles/Project_Default.xml
create mode 100644 .idea/modules.xml
create mode 100644 .idea/vcs.xml
create mode 100644 .idea/yaodongxiao.iml
create mode 100644 App.vue
create mode 100644 api/address.js
create mode 100644 api/bonus.js
create mode 100644 api/cart.js
create mode 100644 api/common.js
create mode 100644 api/coupon.js
create mode 100644 api/goods.js
create mode 100644 api/product.js
create mode 100644 api/reimburse.js
create mode 100644 api/request.js
create mode 100644 api/self-report.js
create mode 100644 api/share.js
create mode 100644 api/shop.js
create mode 100644 api/task.js
create mode 100644 api/user.js
create mode 100644 api/visit.js
create mode 100644 components/AppNavbar.vue
create mode 100644 components/AreaPicker.vue
create mode 100644 components/HeaderItem.vue
create mode 100644 components/Login.vue
create mode 100644 components/TitleItem.vue
create mode 100644 components/customTabbar.vue
create mode 100644 config/color.js
create mode 100644 config/index.js
create mode 100644 index.html
create mode 100644 js_sdk/luch-request/luch-request/adapters/index.js
create mode 100644 js_sdk/luch-request/luch-request/core/InterceptorManager.js
create mode 100644 js_sdk/luch-request/luch-request/core/Request.js
create mode 100644 js_sdk/luch-request/luch-request/core/buildFullPath.js
create mode 100644 js_sdk/luch-request/luch-request/core/defaults.js
create mode 100644 js_sdk/luch-request/luch-request/core/dispatchRequest.js
create mode 100644 js_sdk/luch-request/luch-request/core/mergeConfig.js
create mode 100644 js_sdk/luch-request/luch-request/core/settle.js
create mode 100644 js_sdk/luch-request/luch-request/helpers/buildURL.js
create mode 100644 js_sdk/luch-request/luch-request/helpers/combineURLs.js
create mode 100644 js_sdk/luch-request/luch-request/helpers/isAbsoluteURL.js
create mode 100644 js_sdk/luch-request/luch-request/index.d.ts
create mode 100644 js_sdk/luch-request/luch-request/index.js
create mode 100644 js_sdk/luch-request/luch-request/utils.js
create mode 100644 js_sdk/luch-request/luch-request/utils/clone.js
create mode 100644 js_sdk/luch-request/package.json
create mode 100644 js_sdk/qqmap-wx-jssdk/qqmap-wx-jssdk.js
create mode 100644 js_sdk/qqmap-wx-jssdk/qqmap-wx-jssdk.min.js
create mode 100644 main.js
create mode 100644 manifest.json
create mode 100644 package-lock.json
create mode 100644 package.json
create mode 100644 packageUser/address/index.vue
create mode 100644 packageUser/chronic/index.vue
create mode 100644 packageUser/chronic/manage.vue
create mode 100644 packageUser/coupon/index.vue
create mode 100644 packageUser/points/index.vue
create mode 100644 packageUser/self-report/create.vue
create mode 100644 packageUser/self-report/records.vue
create mode 100644 packageUser/travel/index.vue
create mode 100644 pages.json
create mode 100644 pages/audit/components/AuditMovementItem.vue
create mode 100644 pages/audit/components/AuditSelfReportItem.vue
create mode 100644 pages/audit/components/AuditShareItem.vue
create mode 100644 pages/audit/components/AuditStoreVisitItem.vue
create mode 100644 pages/audit/components/record.scss
create mode 100644 pages/audit/index.vue
create mode 100644 pages/auth/login.vue
create mode 100644 pages/bonus/index.vue
create mode 100644 pages/bonus/withdraw-record.vue
create mode 100644 pages/bonus/withdraw.vue
create mode 100644 pages/cart/index.vue
create mode 100644 pages/certification/index.vue
create mode 100644 pages/exchange/index.vue
create mode 100755 pages/index/components/Progress-Bar/Progress-Bar.css
create mode 100644 pages/index/components/Progress-Bar/Progress-Bar.vue
create mode 100644 pages/index/index.vue
create mode 100644 pages/mine/index.vue
create mode 100644 pages/mine/profile.vue
create mode 100644 pages/order/checkout.vue
create mode 100644 pages/order/detail.vue
create mode 100644 pages/order/list.vue
create mode 100644 pages/performance/index.vue
create mode 100644 pages/product/detail.vue
create mode 100644 pages/share/components/ShareCreatePopup.vue
create mode 100644 pages/share/create.vue
create mode 100644 pages/share/detail.vue
create mode 100644 pages/share/index.vue
create mode 100644 pages/share/mine.vue
create mode 100644 pages/task/detail.vue
create mode 100644 pages/visit/index.vue
create mode 100644 static/images/addCart.png
create mode 100644 static/images/blob-scene-haikei.png
create mode 100644 static/images/coupon.png
create mode 100644 static/logo.png
create mode 100644 static/style/common.scss
create mode 100644 static/tabbar/group-active.png
create mode 100644 static/tabbar/group.png
create mode 100644 static/tabbar/home-active.png
create mode 100644 static/tabbar/home.png
create mode 100644 static/tabbar/share-active.png
create mode 100644 static/tabbar/share.png
create mode 100644 static/tabbar/switch-active.png
create mode 100644 static/tabbar/switch.png
create mode 100644 static/tabbar/user-active.png
create mode 100644 static/tabbar/user.png
create mode 100644 uni.promisify.adaptor.js
create mode 100644 uni.scss
create mode 100644 util/index.js
create mode 100644 util/order.js
create mode 100644 util/user.js
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac7cd59
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+/node_modules/
+/unpackage/
+/uni_modules/
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/UniappTool.xml b/.idea/UniappTool.xml
new file mode 100644
index 0000000..c859f7e
--- /dev/null
+++ b/.idea/UniappTool.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..9ae2fa3
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..fffee1a
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/yaodongxiao.iml b/.idea/yaodongxiao.iml
new file mode 100644
index 0000000..24643cc
--- /dev/null
+++ b/.idea/yaodongxiao.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/App.vue b/App.vue
new file mode 100644
index 0000000..a5d5c01
--- /dev/null
+++ b/App.vue
@@ -0,0 +1,127 @@
+
+
+
diff --git a/api/address.js b/api/address.js
new file mode 100644
index 0000000..4dd8d7d
--- /dev/null
+++ b/api/address.js
@@ -0,0 +1,5 @@
+import post, {get} from "@/api/request";
+
+export const userAddressListReq = params => get('/shop/user-address', {params})
+
+export const saveUserAddressReq = data => post('/shop/user-address', data)
diff --git a/api/bonus.js b/api/bonus.js
new file mode 100644
index 0000000..6b38161
--- /dev/null
+++ b/api/bonus.js
@@ -0,0 +1,7 @@
+import post, {get} from "@/api/request";
+
+export const userBalanceLogListReq = data => post('/shop/user-balance-log/list', data)
+
+export const dealerWithdrawPageReq = params => get('/shop/dealer-withdraw/page', {params})
+
+export const dealerWithdrawAddReq = data => post('/shop/dealer-withdraw', data)
diff --git a/api/cart.js b/api/cart.js
new file mode 100644
index 0000000..3d4e370
--- /dev/null
+++ b/api/cart.js
@@ -0,0 +1,5 @@
+import post, {get} from "@/api/request";
+
+export const cartListReq = params => get('/shop/cart', {params})
+
+export const addCartReq = data => post('/shop/cart', data)
diff --git a/api/common.js b/api/common.js
new file mode 100644
index 0000000..e94a0b0
--- /dev/null
+++ b/api/common.js
@@ -0,0 +1,55 @@
+import post, {get} from "@/api/request";
+
+export const areaListReq = data => post('/common/area/list-by-level', data)
+
+export const areaInfoReq = data => post('/common/area/info', data)
+
+export const areaInfoByNameReq = data => post('/common/area/area-info-by-name', data)
+
+export const areaListByFidReq = data => post('/common/area/listByFid', data)
+
+export const indexListReq = data => post('/com/publish-list/list', data)
+
+export const swiperListReq = () => get('/sys/sys-swiper')
+
+export const dictListReq = data => post('/system/dict/info', data)
+
+export const dictDataInfoReq = id => get(`/system/dict-data/${id}`)
+
+export const dictDataListByDictReq = data => post(`/system/dict-data/list-by-dict`, data)
+
+export const addViewNumReq = data => post('/shop/user-browse/add', data)
+
+export const sysSettingInfoReq = data => post('/sys', data)
+
+export const dictDataReq = params => get('/system/dict-data', {params})
+
+export const listArticleCateReq = params => get('/cms/article-category', {params})
+
+export const listArticleReq = params => get('/cms/article', {params})
+
+export const pageArticleReq = params => get('/cms/article/page', {params})
+
+export const articleInfoReq = id => get(`/cms/article/${id}`)
+
+export const adListReq = () => get(`/cms/cms-ad`)
+
+export const addArticleCollectReq = data => post(`/cms/article-collect`, data)
+
+export const addFeedbackReq = data => post(`/gx/gx-feedback`, data)
+
+export const listArticleCollectReq = () => get(`/cms/article-collect`)
+
+export const sysAreaListReq = params => get(`/sys/sys-area`, {params})
+
+export const articleCommentListReq = params => get(`/cms/article-comment`, {params})
+
+export const addArticleCommentReq = data => post(`/cms/article-comment`, data)
+
+export const aiReq = data => post(`/yxe/ai`, data)
+
+export const aiAliReq = data => post(`/yxe/ai/ali`, data)
+
+export const aiGetAddressReq = data => post(`/yxe/ai/getAddress`, data)
+
+export const yxeServiceReq = () => get(`/yxe/yxe-service`)
diff --git a/api/coupon.js b/api/coupon.js
new file mode 100644
index 0000000..fe5d35a
--- /dev/null
+++ b/api/coupon.js
@@ -0,0 +1,7 @@
+import post, {get, put} from "@/api/request";
+
+export const couponListReq = params => get('/shop/coupon', {params})
+
+export const userCouponListReq = params => get('/shop/user-coupon/userList', {params})
+
+export const userCouponAddReq = data => post('/shop/user-coupon', data)
diff --git a/api/goods.js b/api/goods.js
new file mode 100644
index 0000000..28ed05e
--- /dev/null
+++ b/api/goods.js
@@ -0,0 +1,19 @@
+import post, {get} from "@/api/request";
+
+export const goodsCategoryListReq = params => get('/shop/goods-category', {params})
+
+export const goodsListReq = params => get('/shop/goods', {params})
+
+export const goodsListByIdsReq = data => post('/shop/goods/listByIds', data)
+
+export const goodsInfoReq = id => get(`/shop/goods/${id}`)
+
+export const orderListReq = params => get('/shop/order', {params})
+
+export const orderInfoReq = id => get(`/shop/order/${id}`)
+
+export const orderAddReq = data => post('/shop/order', data)
+
+export const cartAddReq = data => post('/shop/cart', data)
+
+export const cartListReq = params => get('/shop/cart', {params})
diff --git a/api/product.js b/api/product.js
new file mode 100644
index 0000000..cabfe4d
--- /dev/null
+++ b/api/product.js
@@ -0,0 +1,11 @@
+import {get} from "@/api/request";
+
+export const productGroupReq = params => get('/yxe/yxe-product/group', {params})
+
+export const productListReq = params => get('/yxe/yxe-product', {params})
+
+export const productGroupByChainReq = params => get('/yxe/yxe-product/groupByChain', {params})
+
+export const productGroupByCateReq = params => get('/yxe/yxe-product/groupByCate', {params})
+
+export const chronicDiseaseProductListReq = params => get('/yxe/yxe-chronic-disease-product', {params})
diff --git a/api/reimburse.js b/api/reimburse.js
new file mode 100644
index 0000000..3331b5c
--- /dev/null
+++ b/api/reimburse.js
@@ -0,0 +1,11 @@
+import post, {get, put} from "@/api/request";
+
+export const reimburseAddReq = data => post('/yxe/yxe-reimburse', data)
+
+export const reimburseUserListReq = params => get('/yxe/yxe-reimburse/userList', {params})
+
+export const chronicDiseaseCollectionAddReq = data => post('/yxe/yxe-chronic-disease-collection', data)
+
+export const chronicDiseaseCollectionListReq = params => get('/yxe/yxe-chronic-disease-collection', {params})
+
+export const chronicDiseaseCollectionChangeReq = data => put('/yxe/yxe-chronic-disease-collection', data)
diff --git a/api/request.js b/api/request.js
new file mode 100644
index 0000000..83b7f91
--- /dev/null
+++ b/api/request.js
@@ -0,0 +1,106 @@
+import Request from '@/js_sdk/luch-request/luch-request/index.js'
+import {API} from '@/config'
+import {getUserInfo, clearUserInfo} from "@/util/user";
+
+const http = new Request();
+http.config.baseURL = API
+
+http.interceptors.request.use((config) => {
+ // uni.showLoading({title: '加载中'})
+ const token = getUserInfo().token
+ if (token && token !== 'undefined') {
+ config.header.Authorization = token
+ }
+ config.header.tenantId = 10325
+ return config
+}, config => {
+ return Promise.reject(config)
+})
+
+http.interceptors.response.use((response) => {
+ const url = response.config.url
+ // uni.hideLoading()
+ switch (parseInt(response.data.code)) {
+ case 401: {
+ if (['user-cart-num', 'new-order', 'user-cart-add', 'user-info'].indexOf(url) === -1) {
+ uni.showToast({
+ title: '请登录',
+ icon: 'error',
+ duration: 2000,
+ })
+ setTimeout(() => {
+ clearUserInfo()
+ }, 2000)
+ }
+ }
+ break;
+ case 500: {
+ if (['/user/change-pay-password', 'new-order-from-goods-list'].indexOf(url) === -1) {
+ console.log(response.data)
+ uni.showToast({
+ icon: 'none',
+ title: response.data.content.msg,
+ duration: 2000,
+ })
+ }
+ }
+ break;
+ case 502: {
+ uni.showToast({
+ icon: 'error',
+ title: '系统维护中',
+ duration: 2000,
+ })
+ }
+ }
+ return response
+}, (error) => {
+ if (error.code === 502) {
+ uni.showToast({
+ icon: 'error',
+ title: '系统维护中',
+ duration: 2000,
+ })
+ }
+ return Promise.reject(error)
+})
+
+const post = (url, formData, options) => {
+ return new Promise((resolve, reject) => {
+ http.post(url, formData, options).then(res => {
+ if (res.data.code === 0) resolve(res.data)
+ else if (res.data.code === 401) reject(res.data)
+ else reject(res.data)
+ })
+ })
+}
+
+export const get = (url, options) => {
+ // console.log(options)
+ return new Promise((resolve, reject) => {
+ http.get(url, options).then(res => {
+ resolve(res.data)
+ })
+ })
+}
+
+export const del = (url, options) => {
+ // console.log(options)
+ return new Promise((resolve, reject) => {
+ http.delete(url, options).then(res => {
+ resolve(res.data)
+ })
+ })
+}
+
+export const put = (url, formData, options) => {
+ return new Promise((resolve, reject) => {
+ http.put(url, formData, options).then(res => {
+ if (res.data.code === 0) resolve(res.data)
+ else if (res.data.code === 401) reject(res.data)
+ else reject(res.data)
+ })
+ })
+}
+
+export default post
\ No newline at end of file
diff --git a/api/self-report.js b/api/self-report.js
new file mode 100644
index 0000000..a736854
--- /dev/null
+++ b/api/self-report.js
@@ -0,0 +1,19 @@
+import post, {get, put, del} from "@/api/request";
+
+export const selfReportAddReq = data => post('/yxe/yxe-self-report', data)
+
+export const selfReportListReq = params => get('/yxe/yxe-self-report', {params})
+
+export const selfReportPageReq = params => get('/yxe/yxe-self-report/page', {params})
+
+export const selfReportAuditPageReq = params => get('/yxe/yxe-self-report/audit/page', {params})
+
+export const selfReportAuditDetailReq = id => get(`/yxe/yxe-self-report/audit/${id}`)
+
+export const selfReportUserListReq = params => get('/yxe/yxe-self-report/userList', {params})
+
+export const selfReportInfoReq = id => get(`/yxe/yxe-self-report/${id}`)
+
+export const selfReportChangeReq = data => put('/yxe/yxe-self-report', data)
+
+export const selfReportDeleteReq = id => del(`/yxe/yxe-self-report/${id}`)
diff --git a/api/share.js b/api/share.js
new file mode 100644
index 0000000..51c946e
--- /dev/null
+++ b/api/share.js
@@ -0,0 +1,27 @@
+import post, {del, get, put} from "@/api/request";
+
+export const shareAddReq = data => post('/yxe/yxe-share', data)
+
+export const shareUserListReq = params => get('/yxe/yxe-share/userList', {params})
+
+export const shareListReq = params => get('/yxe/yxe-share', {params})
+
+export const sharePageReq = params => get('/yxe/yxe-share/page', {params})
+
+export const shareAuditPageReq = params => get('/yxe/yxe-share/audit/page', {params})
+
+export const shareAuditDetailReq = id => get(`/yxe/yxe-share/audit/${id}`)
+
+export const shareChangeReq = data => put('/yxe/yxe-share', data)
+
+export const shareDeleteReq = id => del(`/yxe/yxe-share/${id}`)
+
+export const shareCopyReq = data => post('/yxe/yxe-share/copy', data)
+
+export const shareDataReq = params => get('/yxe/yxe-share/data', {params})
+
+export const shareUserDataReq = () => get('/yxe/yxe-share/userData')
+
+export const shareDetailPageReq = params => get('/yxe/yxe-share-detail/page', {params})
+
+export const productByCodeReq = (code, merchantName) => get(`/yxe/yxe-product/byCode/${code}/${merchantName}`)
diff --git a/api/shop.js b/api/shop.js
new file mode 100644
index 0000000..c8dfa4e
--- /dev/null
+++ b/api/shop.js
@@ -0,0 +1,5 @@
+import {get} from "@/api/request";
+
+export const merchantListReq = params => get('/shop/merchant', {params})
+export const merchantGroupByChainReq = params => get('/shop/merchant/groupByChain', {params})
+export const userPointLogUserListReq = params => get('/shop/user-point-log/userList', {params})
diff --git a/api/task.js b/api/task.js
new file mode 100644
index 0000000..5115345
--- /dev/null
+++ b/api/task.js
@@ -0,0 +1,25 @@
+import post, {get, put} from "@/api/request";
+
+export const taskCateListReq = params => get('/yxe/yxe-task-cate', {params})
+
+export const taskListReq = params => get('/yxe/yxe-task', {params})
+
+export const taskDoneListReq = params => get('/yxe/yxe-task/doneList', {params})
+
+export const taskListForDataReq = params => get('/yxe/yxe-task/listForData', {params})
+
+export const taskInfoReq = id => get(`/yxe/yxe-task/${id}`)
+
+export const addUserTaskReq = data => post(`/yxe/yxe-user-task`, data)
+
+export const listUserTaskReq = params => get(`/yxe/yxe-user-task/page`, {params})
+
+export const auditUserTaskPageReq = params => get(`/yxe/yxe-user-task/audit/page`, {params})
+
+export const changeUserTaskReq = data => put(`/yxe/yxe-user-task`, data)
+
+export const copyUserTaskReq = data => post(`/yxe/yxe-user-task/copy`, data)
+
+export const taskDataReq = () => get(`/yxe/yxe-task/data`)
+
+export const taskUserRankReq = () => get(`/yxe/yxe-user-task/userRank`)
diff --git a/api/user.js b/api/user.js
new file mode 100644
index 0000000..9e27b00
--- /dev/null
+++ b/api/user.js
@@ -0,0 +1,34 @@
+import post, {get, put} from "@/api/request";
+
+export const loginReq = data => post('/wx-login/getOpenId', data)
+
+export const loginByPhoneReq = data => post('/wx-login/loginByMpWxPhone', data)
+
+export const loginByPhoneForTestReq = data => post('/system/user/loginByPhoneForTest', data)
+
+export const userInfoReq = () => get('/auth/user')
+
+export const updateUserReq = data => put('/system/user/self', data)
+
+export const countUserIncomeReq = () => get('/system/user/countUserIncome')
+
+export const userInfoUpdateReq = data => post('/system/user/update', data)
+
+export const userInfoUpdateDataReq = data => post('/system/user/update-data', data)
+
+export const userSearchReq = params => get('/system/user', {params})
+
+export const userSetCardReq = data => post('/user/set-card', data)
+
+export const userCardChargeReq = data => post('/user/card-charge', data)
+
+export const userCardReduceReq = data => post('/user/card-reduce', data)
+
+export const userCardUseReq = data => post('/user/card-use', data)
+
+export const dealerWithdrawAddReq = data => post('/shop/dealer-withdraw', data)
+
+export const updateDealerWithdraw = data => put('/shop/dealer-withdraw', data)
+
+export const dealerWithdrawListReq = params => get('/shop/dealer-withdraw', {params})
+
diff --git a/api/visit.js b/api/visit.js
new file mode 100644
index 0000000..86037d7
--- /dev/null
+++ b/api/visit.js
@@ -0,0 +1,5 @@
+import post, {get} from "@/api/request";
+
+export const visitAddReq = data => post('/yxe/yxe-visit-list', data)
+
+export const visitListReq = params => get('/yxe/yxe-visit-list', {params})
diff --git a/components/AppNavbar.vue b/components/AppNavbar.vue
new file mode 100644
index 0000000..9e33222
--- /dev/null
+++ b/components/AppNavbar.vue
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+
+ {{ title }}
+
+
+
+
+
+
diff --git a/components/AreaPicker.vue b/components/AreaPicker.vue
new file mode 100644
index 0000000..9816631
--- /dev/null
+++ b/components/AreaPicker.vue
@@ -0,0 +1,250 @@
+
+
+
+
+ 选择省市
+
+ 取消
+ 确定
+
+
+
+ 加载中...
+
+
+
+
+ {{ item.name }}
+
+
+
+
+
+ 暂无城市
+
+ {{ item.name }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/HeaderItem.vue b/components/HeaderItem.vue
new file mode 100644
index 0000000..843e363
--- /dev/null
+++ b/components/HeaderItem.vue
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/components/Login.vue b/components/Login.vue
new file mode 100644
index 0000000..170a62e
--- /dev/null
+++ b/components/Login.vue
@@ -0,0 +1,222 @@
+
+
+
+
+
+
+ 为了提供更优质的服务,需要获取
+
+
+
+
+
+
+
+
+
+ 我已阅读并同意
+
+ 《用户协议》
+
+
+ 《隐私协议》
+
+
+
+
+
+
+
+ 拒绝
+
+
+ 允许
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 我已阅读并同意
+
+ 《用户协议》
+
+
+ 《隐私协议》
+
+
+
+
+
+
+
+ 关闭
+
+
+ 登录
+
+
+
+
+ 手机号登录
+
+ 一键授权登录
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/TitleItem.vue b/components/TitleItem.vue
new file mode 100644
index 0000000..3abe6ac
--- /dev/null
+++ b/components/TitleItem.vue
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/components/customTabbar.vue b/components/customTabbar.vue
new file mode 100644
index 0000000..8d3a454
--- /dev/null
+++ b/components/customTabbar.vue
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/config/color.js b/config/color.js
new file mode 100644
index 0000000..e87bf37
--- /dev/null
+++ b/config/color.js
@@ -0,0 +1,18 @@
+export const YELLOW = '#e2d602'
+export const ORANGE = '#fd7600'
+export const ORANGE_LIGHT = '#FEEDA8'
+export const GREEN = '#2fc6a7'
+export const GREEN_LIGHT = '#96b0a3'
+export const BLUE = '#2563eb'
+export const BLUE_LIGHT = '#049DFF'
+export const RED = '#FE6869'
+export const RED_LIGHT = '#F7B2B2'
+export const PURPLE = '#7F6CDE'
+export const PINK = '#FE5397'
+export const PURPLE_DARK = '#7F6CDE'
+export const PUBLISH = '#FDCB05'
+export const GRAY = '#999'
+export const PURPLE_LIGHT = '#7F6CDE'
+
+export const MAIN = GREEN
+export const LINEAR = 'linear-gradient(90deg, #049DFF, #2665c4)'
diff --git a/config/index.js b/config/index.js
new file mode 100644
index 0000000..3783f3c
--- /dev/null
+++ b/config/index.js
@@ -0,0 +1,23 @@
+const MAIN_API_FN = () => {
+ if (process.env.NODE_ENV === 'development') {
+ // return 'http://127.0.0.1:10051'
+ return 'https://yxe-api.gxwebsoft.com'
+ } else {
+ return 'https://yxe-api.gxwebsoft.com'
+ }
+}
+
+const MAIN_WS_FN = () => {
+ if (process.env.NODE_ENV === 'development') {
+ return 'ws://127.0.0.1:10051/chat'
+ // return 'wss://guoxue.gxwebsoft.com/chat'
+ } else {
+ return 'wss://yxe-api.gxwebsoft.com'
+ }
+}
+
+export const MAIN_API = MAIN_API_FN()
+
+export const WS_API = MAIN_WS_FN()
+
+export const API = `${MAIN_API}/api`
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..b5d330d
--- /dev/null
+++ b/index.html
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/js_sdk/luch-request/luch-request/adapters/index.js b/js_sdk/luch-request/luch-request/adapters/index.js
new file mode 100644
index 0000000..9bcd653
--- /dev/null
+++ b/js_sdk/luch-request/luch-request/adapters/index.js
@@ -0,0 +1,100 @@
+import buildURL from '../helpers/buildURL'
+import buildFullPath from '../core/buildFullPath'
+import settle from '../core/settle'
+import {isUndefined} from "../utils"
+
+/**
+ * 返回可选值存在的配置
+ * @param {Array} keys - 可选值数组
+ * @param {Object} config2 - 配置
+ * @return {{}} - 存在的配置项
+ */
+const mergeKeys = (keys, config2) => {
+ let config = {}
+ keys.forEach(prop => {
+ if (!isUndefined(config2[prop])) {
+ config[prop] = config2[prop]
+ }
+ })
+ return config
+}
+export default (config) => {
+ return new Promise((resolve, reject) => {
+ let fullPath = buildURL(buildFullPath(config.baseURL, config.url), config.params, config.paramsSerializer)
+ const _config = {
+ url: fullPath,
+ header: config.header,
+ complete: (response) => {
+ config.fullPath = fullPath
+ response.config = config
+ response.rawData = response.data
+ try {
+ // 对可能字符串不是json 的情况容错
+ if (typeof response.data === 'string') {
+ response.data = JSON.parse(response.data)
+ }
+ // eslint-disable-next-line no-empty
+ } catch (e) {
+ }
+ settle(resolve, reject, response)
+ }
+ }
+ let requestTask
+ if (config.method === 'UPLOAD') {
+ delete _config.header['content-type']
+ delete _config.header['Content-Type']
+ let otherConfig = {
+ // #ifdef MP-ALIPAY
+ fileType: config.fileType,
+ // #endif
+ filePath: config.filePath,
+ name: config.name
+ }
+ const optionalKeys = [
+ // #ifdef APP-PLUS || H5
+ 'files',
+ // #endif
+ // #ifdef H5
+ 'file',
+ // #endif
+ // #ifdef H5 || APP-PLUS
+ 'timeout',
+ // #endif
+ 'formData'
+ ]
+ requestTask = uni.uploadFile({..._config, ...otherConfig, ...mergeKeys(optionalKeys, config)})
+ } else if (config.method === 'DOWNLOAD') {
+ // #ifdef H5 || APP-PLUS
+ if (!isUndefined(config['timeout'])) {
+ _config['timeout'] = config['timeout']
+ }
+ // #endif
+ requestTask = uni.downloadFile(_config)
+ } else {
+ const optionalKeys = [
+ 'data',
+ 'method',
+ // #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
+ 'timeout',
+ // #endif
+ 'dataType',
+ // #ifndef MP-ALIPAY
+ 'responseType',
+ // #endif
+ // #ifdef APP-PLUS
+ 'sslVerify',
+ // #endif
+ // #ifdef H5
+ 'withCredentials',
+ // #endif
+ // #ifdef APP-PLUS
+ 'firstIpv4',
+ // #endif
+ ]
+ requestTask = uni.request({..._config, ...mergeKeys(optionalKeys, config)})
+ }
+ if (config.getTask) {
+ config.getTask(requestTask, config)
+ }
+ })
+}
diff --git a/js_sdk/luch-request/luch-request/core/InterceptorManager.js b/js_sdk/luch-request/luch-request/core/InterceptorManager.js
new file mode 100644
index 0000000..3ea0d5e
--- /dev/null
+++ b/js_sdk/luch-request/luch-request/core/InterceptorManager.js
@@ -0,0 +1,51 @@
+'use strict'
+
+
+function InterceptorManager() {
+ this.handlers = []
+}
+
+/**
+ * Add a new interceptor to the stack
+ *
+ * @param {Function} fulfilled The function to handle `then` for a `Promise`
+ * @param {Function} rejected The function to handle `reject` for a `Promise`
+ *
+ * @return {Number} An ID used to remove interceptor later
+ */
+InterceptorManager.prototype.use = function use(fulfilled, rejected) {
+ this.handlers.push({
+ fulfilled: fulfilled,
+ rejected: rejected
+ })
+ return this.handlers.length - 1
+}
+
+/**
+ * Remove an interceptor from the stack
+ *
+ * @param {Number} id The ID that was returned by `use`
+ */
+InterceptorManager.prototype.eject = function eject(id) {
+ if (this.handlers[id]) {
+ this.handlers[id] = null
+ }
+}
+
+/**
+ * Iterate over all the registered interceptors
+ *
+ * This method is particularly useful for skipping over any
+ * interceptors that may have become `null` calling `eject`.
+ *
+ * @param {Function} fn The function to call for each interceptor
+ */
+InterceptorManager.prototype.forEach = function forEach(fn) {
+ this.handlers.forEach(h => {
+ if (h !== null) {
+ fn(h)
+ }
+ })
+}
+
+export default InterceptorManager
diff --git a/js_sdk/luch-request/luch-request/core/Request.js b/js_sdk/luch-request/luch-request/core/Request.js
new file mode 100644
index 0000000..d11f152
--- /dev/null
+++ b/js_sdk/luch-request/luch-request/core/Request.js
@@ -0,0 +1,197 @@
+/**
+ * @Class Request
+ * @description luch-request http请求插件
+ * @Author lu-ch
+ * @Email webwork.s@qq.com
+ * 文档: https://www.quanzhan.co/luch-request/
+ * github: https://github.com/lei-mu/luch-request
+ * DCloud: http://ext.dcloud.net.cn/plugin?id=392
+ */
+
+
+import dispatchRequest from './dispatchRequest'
+import InterceptorManager from './InterceptorManager'
+import mergeConfig from './mergeConfig'
+import defaults from './defaults'
+import { isPlainObject } from '../utils'
+import clone from '../utils/clone'
+
+export default class Request {
+ /**
+ * @param {Object} arg - 全局配置
+ * @param {String} arg.baseURL - 全局根路径
+ * @param {Object} arg.header - 全局header
+ * @param {String} arg.method = [GET|POST|PUT|DELETE|CONNECT|HEAD|OPTIONS|TRACE] - 全局默认请求方式
+ * @param {String} arg.dataType = [json] - 全局默认的dataType
+ * @param {String} arg.responseType = [text|arraybuffer] - 全局默认的responseType。支付宝小程序不支持
+ * @param {Object} arg.custom - 全局默认的自定义参数
+ * @param {Number} arg.timeout - 全局默认的超时时间,单位 ms。默认60000。H5(HBuilderX 2.9.9+)、APP(HBuilderX 2.9.9+)、微信小程序(2.10.0)、支付宝小程序
+ * @param {Boolean} arg.sslVerify - 全局默认的是否验证 ssl 证书。默认true.仅App安卓端支持(HBuilderX 2.3.3+)
+ * @param {Boolean} arg.withCredentials - 全局默认的跨域请求时是否携带凭证(cookies)。默认false。仅H5支持(HBuilderX 2.6.15+)
+ * @param {Boolean} arg.firstIpv4 - 全DNS解析时优先使用ipv4。默认false。仅 App-Android 支持 (HBuilderX 2.8.0+)
+ * @param {Function(statusCode):Boolean} arg.validateStatus - 全局默认的自定义验证器。默认statusCode >= 200 && statusCode < 300
+ */
+ constructor(arg = {}) {
+ if (!isPlainObject(arg)) {
+ arg = {}
+ console.warn('设置全局参数必须接收一个Object')
+ }
+ this.config = clone({...defaults, ...arg})
+ this.interceptors = {
+ request: new InterceptorManager(),
+ response: new InterceptorManager()
+ }
+ }
+
+ /**
+ * @Function
+ * @param {Request~setConfigCallback} f - 设置全局默认配置
+ */
+ setConfig(f) {
+ this.config = f(this.config)
+ }
+
+ middleware(config) {
+ config = mergeConfig(this.config, config)
+ let chain = [dispatchRequest, undefined]
+ let promise = Promise.resolve(config)
+
+ this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
+ chain.unshift(interceptor.fulfilled, interceptor.rejected)
+ })
+
+ this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
+ chain.push(interceptor.fulfilled, interceptor.rejected)
+ })
+
+ while (chain.length) {
+ promise = promise.then(chain.shift(), chain.shift())
+ }
+
+ return promise
+ }
+
+ /**
+ * @Function
+ * @param {Object} config - 请求配置项
+ * @prop {String} options.url - 请求路径
+ * @prop {Object} options.data - 请求参数
+ * @prop {Object} [options.responseType = config.responseType] [text|arraybuffer] - 响应的数据类型
+ * @prop {Object} [options.dataType = config.dataType] - 如果设为 json,会尝试对返回的数据做一次 JSON.parse
+ * @prop {Object} [options.header = config.header] - 请求header
+ * @prop {Object} [options.method = config.method] - 请求方法
+ * @returns {Promise}
+ */
+ request(config = {}) {
+ return this.middleware(config)
+ }
+
+ get(url, options = {}) {
+ return this.middleware({
+ url,
+ method: 'GET',
+ ...options
+ })
+ }
+
+ post(url, data, options = {}) {
+ return this.middleware({
+ url,
+ data,
+ method: 'POST',
+ ...options
+ })
+ }
+
+ // #ifndef MP-ALIPAY || MP-KUAISHOU || MP-JD
+ put(url, data, options = {}) {
+ return this.middleware({
+ url,
+ data,
+ method: 'PUT',
+ ...options
+ })
+ }
+
+ // #endif
+
+ // #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
+ delete(url, data, options = {}) {
+ return this.middleware({
+ url,
+ data,
+ method: 'DELETE',
+ ...options
+ })
+ }
+
+ // #endif
+
+ // #ifdef H5 || MP-WEIXIN
+ connect(url, data, options = {}) {
+ return this.middleware({
+ url,
+ data,
+ method: 'CONNECT',
+ ...options
+ })
+ }
+
+ // #endif
+
+ // #ifdef H5 || MP-WEIXIN || MP-BAIDU
+ head(url, data, options = {}) {
+ return this.middleware({
+ url,
+ data,
+ method: 'HEAD',
+ ...options
+ })
+ }
+
+ // #endif
+
+ // #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
+ options(url, data, options = {}) {
+ return this.middleware({
+ url,
+ data,
+ method: 'OPTIONS',
+ ...options
+ })
+ }
+
+ // #endif
+
+ // #ifdef H5 || MP-WEIXIN
+ trace(url, data, options = {}) {
+ return this.middleware({
+ url,
+ data,
+ method: 'TRACE',
+ ...options
+ })
+ }
+
+ // #endif
+
+ upload(url, config = {}) {
+ config.url = url
+ config.method = 'UPLOAD'
+ return this.middleware(config)
+ }
+
+ download(url, config = {}) {
+ config.url = url
+ config.method = 'DOWNLOAD'
+ return this.middleware(config)
+ }
+}
+
+
+/**
+ * setConfig回调
+ * @return {Object} - 返回操作后的config
+ * @callback Request~setConfigCallback
+ * @param {Object} config - 全局默认config
+ */
diff --git a/js_sdk/luch-request/luch-request/core/buildFullPath.js b/js_sdk/luch-request/luch-request/core/buildFullPath.js
new file mode 100644
index 0000000..f2852f4
--- /dev/null
+++ b/js_sdk/luch-request/luch-request/core/buildFullPath.js
@@ -0,0 +1,20 @@
+'use strict'
+
+import isAbsoluteURL from '../helpers/isAbsoluteURL'
+import combineURLs from '../helpers/combineURLs'
+
+/**
+ * Creates a new URL by combining the baseURL with the requestedURL,
+ * only when the requestedURL is not already an absolute URL.
+ * If the requestURL is absolute, this function returns the requestedURL untouched.
+ *
+ * @param {string} baseURL The base URL
+ * @param {string} requestedURL Absolute or relative URL to combine
+ * @returns {string} The combined full path
+ */
+export default function buildFullPath(baseURL, requestedURL) {
+ if (baseURL && !isAbsoluteURL(requestedURL)) {
+ return combineURLs(baseURL, requestedURL)
+ }
+ return requestedURL
+}
diff --git a/js_sdk/luch-request/luch-request/core/defaults.js b/js_sdk/luch-request/luch-request/core/defaults.js
new file mode 100644
index 0000000..ef5a0d9
--- /dev/null
+++ b/js_sdk/luch-request/luch-request/core/defaults.js
@@ -0,0 +1,31 @@
+/**
+ * 默认的全局配置
+ */
+
+
+export default {
+ baseURL: '',
+ header: {},
+ method: 'GET',
+ dataType: 'json',
+ paramsSerializer: null,
+ // #ifndef MP-ALIPAY
+ responseType: 'text',
+ // #endif
+ custom: {},
+ // #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
+ timeout: 60000,
+ // #endif
+ // #ifdef APP-PLUS
+ sslVerify: true,
+ // #endif
+ // #ifdef H5
+ withCredentials: false,
+ // #endif
+ // #ifdef APP-PLUS
+ firstIpv4: false,
+ // #endif
+ validateStatus: function validateStatus(status) {
+ return status >= 200 && status < 300
+ }
+}
diff --git a/js_sdk/luch-request/luch-request/core/dispatchRequest.js b/js_sdk/luch-request/luch-request/core/dispatchRequest.js
new file mode 100644
index 0000000..c5f2c85
--- /dev/null
+++ b/js_sdk/luch-request/luch-request/core/dispatchRequest.js
@@ -0,0 +1,6 @@
+import adapter from '../adapters/index'
+
+
+export default (config) => {
+ return adapter(config)
+}
diff --git a/js_sdk/luch-request/luch-request/core/mergeConfig.js b/js_sdk/luch-request/luch-request/core/mergeConfig.js
new file mode 100644
index 0000000..ca3f10c
--- /dev/null
+++ b/js_sdk/luch-request/luch-request/core/mergeConfig.js
@@ -0,0 +1,103 @@
+import {deepMerge, isUndefined} from '../utils'
+
+/**
+ * 合并局部配置优先的配置,如果局部有该配置项则用局部,如果全局有该配置项则用全局
+ * @param {Array} keys - 配置项
+ * @param {Object} globalsConfig - 当前的全局配置
+ * @param {Object} config2 - 局部配置
+ * @return {{}}
+ */
+const mergeKeys = (keys, globalsConfig, config2) => {
+ let config = {}
+ keys.forEach(prop => {
+ if (!isUndefined(config2[prop])) {
+ config[prop] = config2[prop]
+ } else if (!isUndefined(globalsConfig[prop])) {
+ config[prop] = globalsConfig[prop]
+ }
+ })
+ return config
+}
+/**
+ *
+ * @param globalsConfig - 当前实例的全局配置
+ * @param config2 - 当前的局部配置
+ * @return - 合并后的配置
+ */
+export default (globalsConfig, config2 = {}) => {
+ const method = config2.method || globalsConfig.method || 'GET'
+ let config = {
+ baseURL: config2.baseURL || globalsConfig.baseURL || '',
+ method: method,
+ url: config2.url || '',
+ params: config2.params || {},
+ custom: {...(globalsConfig.custom || {}), ...(config2.custom || {})},
+ header: deepMerge(globalsConfig.header || {}, config2.header || {})
+ }
+ const defaultToConfig2Keys = ['getTask', 'validateStatus', 'paramsSerializer']
+ config = {...config, ...mergeKeys(defaultToConfig2Keys, globalsConfig, config2)}
+
+ // eslint-disable-next-line no-empty
+ if (method === 'DOWNLOAD') {
+ // #ifdef H5 || APP-PLUS
+ if (!isUndefined(config2.timeout)) {
+ config['timeout'] = config2['timeout']
+ } else if (!isUndefined(globalsConfig.timeout)) {
+ config['timeout'] = globalsConfig['timeout']
+ }
+ // #endif
+ } else if (method === 'UPLOAD') {
+ delete config.header['content-type']
+ delete config.header['Content-Type']
+ const uploadKeys = [
+ // #ifdef APP-PLUS || H5
+ 'files',
+ // #endif
+ // #ifdef MP-ALIPAY
+ 'fileType',
+ // #endif
+ // #ifdef H5
+ 'file',
+ // #endif
+ 'filePath',
+ 'name',
+ // #ifdef H5 || APP-PLUS
+ 'timeout',
+ // #endif
+ 'formData',
+ ]
+ uploadKeys.forEach(prop => {
+ if (!isUndefined(config2[prop])) {
+ config[prop] = config2[prop]
+ }
+ })
+ // #ifdef H5 || APP-PLUS
+ if (isUndefined(config.timeout) && !isUndefined(globalsConfig.timeout)) {
+ config['timeout'] = globalsConfig['timeout']
+ }
+ // #endif
+ } else {
+ const defaultsKeys = [
+ 'data',
+ // #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
+ 'timeout',
+ // #endif
+ 'dataType',
+ // #ifndef MP-ALIPAY
+ 'responseType',
+ // #endif
+ // #ifdef APP-PLUS
+ 'sslVerify',
+ // #endif
+ // #ifdef H5
+ 'withCredentials',
+ // #endif
+ // #ifdef APP-PLUS
+ 'firstIpv4',
+ // #endif
+ ]
+ config = {...config, ...mergeKeys(defaultsKeys, globalsConfig, config2)}
+ }
+
+ return config
+}
diff --git a/js_sdk/luch-request/luch-request/core/settle.js b/js_sdk/luch-request/luch-request/core/settle.js
new file mode 100644
index 0000000..b2f1659
--- /dev/null
+++ b/js_sdk/luch-request/luch-request/core/settle.js
@@ -0,0 +1,16 @@
+/**
+ * Resolve or reject a Promise based on response status.
+ *
+ * @param {Function} resolve A function that resolves the promise.
+ * @param {Function} reject A function that rejects the promise.
+ * @param {object} response The response.
+ */
+export default function settle(resolve, reject, response) {
+ const validateStatus = response.config.validateStatus
+ const status = response.statusCode
+ if (status && (!validateStatus || validateStatus(status))) {
+ resolve(response)
+ } else {
+ reject(response)
+ }
+}
diff --git a/js_sdk/luch-request/luch-request/helpers/buildURL.js b/js_sdk/luch-request/luch-request/helpers/buildURL.js
new file mode 100644
index 0000000..e90b908
--- /dev/null
+++ b/js_sdk/luch-request/luch-request/helpers/buildURL.js
@@ -0,0 +1,64 @@
+'use strict'
+
+import * as utils from './../utils'
+
+function encode(val) {
+ return encodeURIComponent(val).replace(/%40/gi, '@').replace(/%3A/gi, ':').replace(/%24/g, '$').replace(/%2C/gi, ',').replace(/%20/g, '+').replace(/%5B/gi, '[').replace(/%5D/gi, ']')
+}
+
+/**
+ * Build a URL by appending params to the end
+ *
+ * @param {string} url The base of the url (e.g., http://www.google.com)
+ * @param {object} [params] The params to be appended
+ * @returns {string} The formatted url
+ */
+export default function buildURL(url, params, paramsSerializer) {
+ /*eslint no-param-reassign:0*/
+ if (!params) {
+ return url
+ }
+
+ var serializedParams
+ if (paramsSerializer) {
+ serializedParams = paramsSerializer(params)
+ } else if (utils.isURLSearchParams(params)) {
+ serializedParams = params.toString()
+ } else {
+ var parts = []
+
+ utils.forEach(params, function serialize(val, key) {
+ if (val === null || typeof val === 'undefined') {
+ return
+ }
+
+ if (utils.isArray(val)) {
+ key = key + '[]'
+ } else {
+ val = [val]
+ }
+
+ utils.forEach(val, function parseValue(v) {
+ if (utils.isDate(v)) {
+ v = v.toISOString()
+ } else if (utils.isObject(v)) {
+ v = JSON.stringify(v)
+ }
+ parts.push(encode(key) + '=' + encode(v))
+ })
+ })
+
+ serializedParams = parts.join('&')
+ }
+
+ if (serializedParams) {
+ var hashmarkIndex = url.indexOf('#')
+ if (hashmarkIndex !== -1) {
+ url = url.slice(0, hashmarkIndex)
+ }
+
+ url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams
+ }
+
+ return url
+}
diff --git a/js_sdk/luch-request/luch-request/helpers/combineURLs.js b/js_sdk/luch-request/luch-request/helpers/combineURLs.js
new file mode 100644
index 0000000..7b9d1ef
--- /dev/null
+++ b/js_sdk/luch-request/luch-request/helpers/combineURLs.js
@@ -0,0 +1,14 @@
+'use strict'
+
+/**
+ * Creates a new URL by combining the specified URLs
+ *
+ * @param {string} baseURL The base URL
+ * @param {string} relativeURL The relative URL
+ * @returns {string} The combined URL
+ */
+export default function combineURLs(baseURL, relativeURL) {
+ return relativeURL
+ ? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '')
+ : baseURL
+}
diff --git a/js_sdk/luch-request/luch-request/helpers/isAbsoluteURL.js b/js_sdk/luch-request/luch-request/helpers/isAbsoluteURL.js
new file mode 100644
index 0000000..2a82517
--- /dev/null
+++ b/js_sdk/luch-request/luch-request/helpers/isAbsoluteURL.js
@@ -0,0 +1,14 @@
+'use strict'
+
+/**
+ * Determines whether the specified URL is absolute
+ *
+ * @param {string} url The URL to test
+ * @returns {boolean} True if the specified URL is absolute, otherwise false
+ */
+export default function isAbsoluteURL(url) {
+ // A URL is considered absolute if it begins with "://" or "//" (protocol-relative URL).
+ // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed
+ // by any combination of letters, digits, plus, period, or hyphen.
+ return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(url)
+}
diff --git a/js_sdk/luch-request/luch-request/index.d.ts b/js_sdk/luch-request/luch-request/index.d.ts
new file mode 100644
index 0000000..e56ee41
--- /dev/null
+++ b/js_sdk/luch-request/luch-request/index.d.ts
@@ -0,0 +1,120 @@
+type AnyObject = Record
+type HttpPromise = Promise>;
+type Tasks = UniApp.RequestTask | UniApp.UploadTask | UniApp.DownloadTask
+export interface RequestTask {
+ abort: () => void;
+ offHeadersReceived: () => void;
+ onHeadersReceived: () => void;
+}
+export interface HttpRequestConfig {
+ /** 请求基地址 */
+ baseURL?: string;
+ /** 请求服务器接口地址 */
+ url?: string;
+
+ /** 请求查询参数,自动拼接为查询字符串 */
+ params?: AnyObject;
+ /** 请求体参数 */
+ data?: AnyObject;
+
+ /** 文件对应的 key */
+ name?: string;
+ /** HTTP 请求中其他额外的 form data */
+ formData?: AnyObject;
+ /** 要上传文件资源的路径。 */
+ filePath?: string;
+ /** 需要上传的文件列表。使用 files 时,filePath 和 name 不生效,App、H5( 2.6.15+) */
+ files?: Array<{
+ name?: string;
+ file?: File;
+ uri: string;
+ }>;
+ /** 要上传的文件对象,仅H5(2.6.15+)支持 */
+ file?: File;
+
+ /** 请求头信息 */
+ header?: AnyObject;
+ /** 请求方式 */
+ method?: "GET" | "POST" | "PUT" | "DELETE" | "CONNECT" | "HEAD" | "OPTIONS" | "TRACE" | "UPLOAD" | "DOWNLOAD";
+ /** 如果设为 json,会尝试对返回的数据做一次 JSON.parse */
+ dataType?: string;
+ /** 设置响应的数据类型,支付宝小程序不支持 */
+ responseType?: "text" | "arraybuffer";
+ /** 自定义参数 */
+ custom?: AnyObject;
+ /** 超时时间,仅微信小程序(2.10.0)、支付宝小程序支持 */
+ timeout?: number;
+ /** DNS解析时优先使用ipv4,仅 App-Android 支持 (HBuilderX 2.8.0+) */
+ firstIpv4?: boolean;
+ /** 验证 ssl 证书 仅5+App安卓端支持(HBuilderX 2.3.3+) */
+ sslVerify?: boolean;
+ /** 跨域请求时是否携带凭证(cookies)仅H5支持(HBuilderX 2.6.15+) */
+ withCredentials?: boolean;
+
+ /** 返回当前请求的task, options。请勿在此处修改options。 */
+ getTask?: (task: T, options: HttpRequestConfig) => void;
+ /** 全局自定义验证器 */
+ validateStatus?: (statusCode: number) => boolean | void;
+ /** params 参数自定义处理 */
+ paramsSerializer?: (params: AnyObject) => string | void;
+}
+export interface HttpResponse {
+ config: HttpRequestConfig;
+ statusCode: number;
+ cookies: Array;
+ data: T;
+ errMsg: string;
+ header: AnyObject;
+ rawData: T;
+}
+export interface HttpUploadResponse {
+ config: HttpRequestConfig;
+ statusCode: number;
+ data: T;
+ errMsg: string;
+ rawData: T;
+}
+export interface HttpDownloadResponse extends HttpResponse {
+ tempFilePath: string;
+}
+export interface HttpError {
+ config: HttpRequestConfig;
+ statusCode?: number;
+ cookies?: Array;
+ data?: any;
+ errMsg: string;
+ header?: AnyObject;
+}
+export interface HttpInterceptorManager {
+ use(
+ onFulfilled?: (config: V) => Promise | V,
+ onRejected?: (config: E) => Promise | E
+ ): void;
+ eject(id: number): void;
+}
+export abstract class HttpRequestAbstract {
+ constructor(config?: HttpRequestConfig);
+ config: HttpRequestConfig;
+ interceptors: {
+ request: HttpInterceptorManager;
+ response: HttpInterceptorManager;
+ }
+ middleware(config: HttpRequestConfig): HttpPromise;
+ request(config: HttpRequestConfig): HttpPromise;
+ get(url: string, config?: HttpRequestConfig): HttpPromise;
+ upload(url: string, config?: HttpRequestConfig): HttpPromise;
+ delete(url: string, data?: AnyObject, config?: HttpRequestConfig): HttpPromise;
+ head(url: string, data?: AnyObject, config?: HttpRequestConfig): HttpPromise;
+ post(url: string, data?: AnyObject, config?: HttpRequestConfig): HttpPromise;
+ put(url: string, data?: AnyObject, config?: HttpRequestConfig): HttpPromise;
+ connect(url: string, data?: AnyObject, config?: HttpRequestConfig): HttpPromise;
+ options(url: string, data?: AnyObject, config?: HttpRequestConfig): HttpPromise;
+ trace(url: string, data?: AnyObject, config?: HttpRequestConfig): HttpPromise;
+
+ download(url: string, config?: HttpRequestConfig): Promise;
+
+ setConfig(onSend: (config: HttpRequestConfig) => HttpRequestConfig): void;
+}
+
+declare class HttpRequest extends HttpRequestAbstract { }
+export default HttpRequest;
diff --git a/js_sdk/luch-request/luch-request/index.js b/js_sdk/luch-request/luch-request/index.js
new file mode 100644
index 0000000..d8fe348
--- /dev/null
+++ b/js_sdk/luch-request/luch-request/index.js
@@ -0,0 +1,2 @@
+import Request from './core/Request'
+export default Request
diff --git a/js_sdk/luch-request/luch-request/utils.js b/js_sdk/luch-request/luch-request/utils.js
new file mode 100644
index 0000000..0b5bf21
--- /dev/null
+++ b/js_sdk/luch-request/luch-request/utils.js
@@ -0,0 +1,135 @@
+'use strict'
+
+// utils is a library of generic helper functions non-specific to axios
+
+var toString = Object.prototype.toString
+
+/**
+ * Determine if a value is an Array
+ *
+ * @param {Object} val The value to test
+ * @returns {boolean} True if value is an Array, otherwise false
+ */
+export function isArray (val) {
+ return toString.call(val) === '[object Array]'
+}
+
+
+/**
+ * Determine if a value is an Object
+ *
+ * @param {Object} val The value to test
+ * @returns {boolean} True if value is an Object, otherwise false
+ */
+export function isObject (val) {
+ return val !== null && typeof val === 'object'
+}
+
+/**
+ * Determine if a value is a Date
+ *
+ * @param {Object} val The value to test
+ * @returns {boolean} True if value is a Date, otherwise false
+ */
+export function isDate (val) {
+ return toString.call(val) === '[object Date]'
+}
+
+/**
+ * Determine if a value is a URLSearchParams object
+ *
+ * @param {Object} val The value to test
+ * @returns {boolean} True if value is a URLSearchParams object, otherwise false
+ */
+export function isURLSearchParams (val) {
+ return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams
+}
+
+
+/**
+ * Iterate over an Array or an Object invoking a function for each item.
+ *
+ * If `obj` is an Array callback will be called passing
+ * the value, index, and complete array for each item.
+ *
+ * If 'obj' is an Object callback will be called passing
+ * the value, key, and complete object for each property.
+ *
+ * @param {Object|Array} obj The object to iterate
+ * @param {Function} fn The callback to invoke for each item
+ */
+export function forEach (obj, fn) {
+ // Don't bother if no value provided
+ if (obj === null || typeof obj === 'undefined') {
+ return
+ }
+
+ // Force an array if not already something iterable
+ if (typeof obj !== 'object') {
+ /*eslint no-param-reassign:0*/
+ obj = [obj]
+ }
+
+ if (isArray(obj)) {
+ // Iterate over array values
+ for (var i = 0, l = obj.length; i < l; i++) {
+ fn.call(null, obj[i], i, obj)
+ }
+ } else {
+ // Iterate over object keys
+ for (var key in obj) {
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
+ fn.call(null, obj[key], key, obj)
+ }
+ }
+ }
+}
+
+/**
+ * 是否为boolean 值
+ * @param val
+ * @returns {boolean}
+ */
+export function isBoolean(val) {
+ return typeof val === 'boolean'
+}
+
+/**
+ * 是否为真正的对象{} new Object
+ * @param {any} obj - 检测的对象
+ * @returns {boolean}
+ */
+export function isPlainObject(obj) {
+ return Object.prototype.toString.call(obj) === '[object Object]'
+}
+
+
+
+/**
+ * Function equal to merge with the difference being that no reference
+ * to original objects is kept.
+ *
+ * @see merge
+ * @param {Object} obj1 Object to merge
+ * @returns {Object} Result of all merge properties
+ */
+export function deepMerge(/* obj1, obj2, obj3, ... */) {
+ let result = {}
+ function assignValue(val, key) {
+ if (typeof result[key] === 'object' && typeof val === 'object') {
+ result[key] = deepMerge(result[key], val)
+ } else if (typeof val === 'object') {
+ result[key] = deepMerge({}, val)
+ } else {
+ result[key] = val
+ }
+ }
+ for (let i = 0, l = arguments.length; i < l; i++) {
+ forEach(arguments[i], assignValue)
+ }
+ return result
+}
+
+export function isUndefined (val) {
+ return typeof val === 'undefined'
+}
diff --git a/js_sdk/luch-request/luch-request/utils/clone.js b/js_sdk/luch-request/luch-request/utils/clone.js
new file mode 100644
index 0000000..2fee704
--- /dev/null
+++ b/js_sdk/luch-request/luch-request/utils/clone.js
@@ -0,0 +1,264 @@
+/* eslint-disable */
+var clone = (function() {
+ 'use strict';
+
+ function _instanceof(obj, type) {
+ return type != null && obj instanceof type;
+ }
+
+ var nativeMap;
+ try {
+ nativeMap = Map;
+ } catch(_) {
+ // maybe a reference error because no `Map`. Give it a dummy value that no
+ // value will ever be an instanceof.
+ nativeMap = function() {};
+ }
+
+ var nativeSet;
+ try {
+ nativeSet = Set;
+ } catch(_) {
+ nativeSet = function() {};
+ }
+
+ var nativePromise;
+ try {
+ nativePromise = Promise;
+ } catch(_) {
+ nativePromise = function() {};
+ }
+
+ /**
+ * Clones (copies) an Object using deep copying.
+ *
+ * This function supports circular references by default, but if you are certain
+ * there are no circular references in your object, you can save some CPU time
+ * by calling clone(obj, false).
+ *
+ * Caution: if `circular` is false and `parent` contains circular references,
+ * your program may enter an infinite loop and crash.
+ *
+ * @param `parent` - the object to be cloned
+ * @param `circular` - set to true if the object to be cloned may contain
+ * circular references. (optional - true by default)
+ * @param `depth` - set to a number if the object is only to be cloned to
+ * a particular depth. (optional - defaults to Infinity)
+ * @param `prototype` - sets the prototype to be used when cloning an object.
+ * (optional - defaults to parent prototype).
+ * @param `includeNonEnumerable` - set to true if the non-enumerable properties
+ * should be cloned as well. Non-enumerable properties on the prototype
+ * chain will be ignored. (optional - false by default)
+ */
+ function clone(parent, circular, depth, prototype, includeNonEnumerable) {
+ if (typeof circular === 'object') {
+ depth = circular.depth;
+ prototype = circular.prototype;
+ includeNonEnumerable = circular.includeNonEnumerable;
+ circular = circular.circular;
+ }
+ // maintain two arrays for circular references, where corresponding parents
+ // and children have the same index
+ var allParents = [];
+ var allChildren = [];
+
+ var useBuffer = typeof Buffer != 'undefined';
+
+ if (typeof circular == 'undefined')
+ circular = true;
+
+ if (typeof depth == 'undefined')
+ depth = Infinity;
+
+ // recurse this function so we don't reset allParents and allChildren
+ function _clone(parent, depth) {
+ // cloning null always returns null
+ if (parent === null)
+ return null;
+
+ if (depth === 0)
+ return parent;
+
+ var child;
+ var proto;
+ if (typeof parent != 'object') {
+ return parent;
+ }
+
+ if (_instanceof(parent, nativeMap)) {
+ child = new nativeMap();
+ } else if (_instanceof(parent, nativeSet)) {
+ child = new nativeSet();
+ } else if (_instanceof(parent, nativePromise)) {
+ child = new nativePromise(function (resolve, reject) {
+ parent.then(function(value) {
+ resolve(_clone(value, depth - 1));
+ }, function(err) {
+ reject(_clone(err, depth - 1));
+ });
+ });
+ } else if (clone.__isArray(parent)) {
+ child = [];
+ } else if (clone.__isRegExp(parent)) {
+ child = new RegExp(parent.source, __getRegExpFlags(parent));
+ if (parent.lastIndex) child.lastIndex = parent.lastIndex;
+ } else if (clone.__isDate(parent)) {
+ child = new Date(parent.getTime());
+ } else if (useBuffer && Buffer.isBuffer(parent)) {
+ if (Buffer.from) {
+ // Node.js >= 5.10.0
+ child = Buffer.from(parent);
+ } else {
+ // Older Node.js versions
+ child = new Buffer(parent.length);
+ parent.copy(child);
+ }
+ return child;
+ } else if (_instanceof(parent, Error)) {
+ child = Object.create(parent);
+ } else {
+ if (typeof prototype == 'undefined') {
+ proto = Object.getPrototypeOf(parent);
+ child = Object.create(proto);
+ }
+ else {
+ child = Object.create(prototype);
+ proto = prototype;
+ }
+ }
+
+ if (circular) {
+ var index = allParents.indexOf(parent);
+
+ if (index != -1) {
+ return allChildren[index];
+ }
+ allParents.push(parent);
+ allChildren.push(child);
+ }
+
+ if (_instanceof(parent, nativeMap)) {
+ parent.forEach(function(value, key) {
+ var keyChild = _clone(key, depth - 1);
+ var valueChild = _clone(value, depth - 1);
+ child.set(keyChild, valueChild);
+ });
+ }
+ if (_instanceof(parent, nativeSet)) {
+ parent.forEach(function(value) {
+ var entryChild = _clone(value, depth - 1);
+ child.add(entryChild);
+ });
+ }
+
+ for (var i in parent) {
+ var attrs = Object.getOwnPropertyDescriptor(parent, i);
+ if (attrs) {
+ child[i] = _clone(parent[i], depth - 1);
+ }
+
+ try {
+ var objProperty = Object.getOwnPropertyDescriptor(parent, i);
+ if (objProperty.set === 'undefined') {
+ // no setter defined. Skip cloning this property
+ continue;
+ }
+ child[i] = _clone(parent[i], depth - 1);
+ } catch(e){
+ if (e instanceof TypeError) {
+ // when in strict mode, TypeError will be thrown if child[i] property only has a getter
+ // we can't do anything about this, other than inform the user that this property cannot be set.
+ continue
+ } else if (e instanceof ReferenceError) {
+ //this may happen in non strict mode
+ continue
+ }
+ }
+
+ }
+
+ if (Object.getOwnPropertySymbols) {
+ var symbols = Object.getOwnPropertySymbols(parent);
+ for (var i = 0; i < symbols.length; i++) {
+ // Don't need to worry about cloning a symbol because it is a primitive,
+ // like a number or string.
+ var symbol = symbols[i];
+ var descriptor = Object.getOwnPropertyDescriptor(parent, symbol);
+ if (descriptor && !descriptor.enumerable && !includeNonEnumerable) {
+ continue;
+ }
+ child[symbol] = _clone(parent[symbol], depth - 1);
+ Object.defineProperty(child, symbol, descriptor);
+ }
+ }
+
+ if (includeNonEnumerable) {
+ var allPropertyNames = Object.getOwnPropertyNames(parent);
+ for (var i = 0; i < allPropertyNames.length; i++) {
+ var propertyName = allPropertyNames[i];
+ var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName);
+ if (descriptor && descriptor.enumerable) {
+ continue;
+ }
+ child[propertyName] = _clone(parent[propertyName], depth - 1);
+ Object.defineProperty(child, propertyName, descriptor);
+ }
+ }
+
+ return child;
+ }
+
+ return _clone(parent, depth);
+ }
+
+ /**
+ * Simple flat clone using prototype, accepts only objects, usefull for property
+ * override on FLAT configuration object (no nested props).
+ *
+ * USE WITH CAUTION! This may not behave as you wish if you do not know how this
+ * works.
+ */
+ clone.clonePrototype = function clonePrototype(parent) {
+ if (parent === null)
+ return null;
+
+ var c = function () {};
+ c.prototype = parent;
+ return new c();
+ };
+
+// private utility functions
+
+ function __objToStr(o) {
+ return Object.prototype.toString.call(o);
+ }
+ clone.__objToStr = __objToStr;
+
+ function __isDate(o) {
+ return typeof o === 'object' && __objToStr(o) === '[object Date]';
+ }
+ clone.__isDate = __isDate;
+
+ function __isArray(o) {
+ return typeof o === 'object' && __objToStr(o) === '[object Array]';
+ }
+ clone.__isArray = __isArray;
+
+ function __isRegExp(o) {
+ return typeof o === 'object' && __objToStr(o) === '[object RegExp]';
+ }
+ clone.__isRegExp = __isRegExp;
+
+ function __getRegExpFlags(re) {
+ var flags = '';
+ if (re.global) flags += 'g';
+ if (re.ignoreCase) flags += 'i';
+ if (re.multiline) flags += 'm';
+ return flags;
+ }
+ clone.__getRegExpFlags = __getRegExpFlags;
+
+ return clone;
+})();
+
+export default clone
diff --git a/js_sdk/luch-request/package.json b/js_sdk/luch-request/package.json
new file mode 100644
index 0000000..78ad4eb
--- /dev/null
+++ b/js_sdk/luch-request/package.json
@@ -0,0 +1,19 @@
+{
+ "id": "luch-request",
+ "displayName": "luch-request",
+ "version": "3.0.8",
+ "description": "基于Promise开发的跨平台、项目级别的请求库,它有更小的体积,易用的api,方便简单的自定义能力",
+ "keywords": [
+ "request",
+ "axios",
+ "网络请求",
+ "拦截器",
+ "promise"
+ ],
+ "dcloudext": {
+ "category": [
+ "JS SDK",
+ "通用 SDK"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/js_sdk/qqmap-wx-jssdk/qqmap-wx-jssdk.js b/js_sdk/qqmap-wx-jssdk/qqmap-wx-jssdk.js
new file mode 100644
index 0000000..595b65b
--- /dev/null
+++ b/js_sdk/qqmap-wx-jssdk/qqmap-wx-jssdk.js
@@ -0,0 +1,1122 @@
+/**
+ * 微信小程序JavaScriptSDK
+ *
+ * @version 1.2
+ * @date 2019-03-06
+ */
+
+var ERROR_CONF = {
+ KEY_ERR: 311,
+ KEY_ERR_MSG: 'key格式错误',
+ PARAM_ERR: 310,
+ PARAM_ERR_MSG: '请求参数信息有误',
+ SYSTEM_ERR: 600,
+ SYSTEM_ERR_MSG: '系统错误',
+ WX_ERR_CODE: 1000,
+ WX_OK_CODE: 200
+};
+var BASE_URL = 'https://apis.map.qq.com/ws/';
+var URL_SEARCH = BASE_URL + 'place/v1/search';
+var URL_SUGGESTION = BASE_URL + 'place/v1/suggestion';
+var URL_GET_GEOCODER = BASE_URL + 'geocoder/v1/';
+var URL_CITY_LIST = BASE_URL + 'district/v1/list';
+var URL_AREA_LIST = BASE_URL + 'district/v1/getchildren';
+var URL_DISTANCE = BASE_URL + 'distance/v1/';
+var URL_DIRECTION = BASE_URL + 'direction/v1/';
+var MODE = {
+ driving: 'driving',
+ transit: 'transit'
+};
+var EARTH_RADIUS = 6378136.49;
+var Utils = {
+ /**
+ * md5加密方法
+ * 版权所有©2011 Sebastian Tschan,https://blueimp.net
+ */
+ safeAdd(x, y) {
+ var lsw = (x & 0xffff) + (y & 0xffff);
+ var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+ return (msw << 16) | (lsw & 0xffff);
+ },
+ bitRotateLeft(num, cnt) {
+ return (num << cnt) | (num >>> (32 - cnt));
+ },
+ md5cmn(q, a, b, x, s, t) {
+ return this.safeAdd(this.bitRotateLeft(this.safeAdd(this.safeAdd(a, q), this.safeAdd(x, t)), s), b);
+ },
+ md5ff(a, b, c, d, x, s, t) {
+ return this.md5cmn((b & c) | (~b & d), a, b, x, s, t);
+ },
+ md5gg(a, b, c, d, x, s, t) {
+ return this.md5cmn((b & d) | (c & ~d), a, b, x, s, t);
+ },
+ md5hh(a, b, c, d, x, s, t) {
+ return this.md5cmn(b ^ c ^ d, a, b, x, s, t);
+ },
+ md5ii(a, b, c, d, x, s, t) {
+ return this.md5cmn(c ^ (b | ~d), a, b, x, s, t);
+ },
+ binlMD5(x, len) {
+ /* append padding */
+ x[len >> 5] |= 0x80 << (len % 32);
+ x[((len + 64) >>> 9 << 4) + 14] = len;
+
+ var i;
+ var olda;
+ var oldb;
+ var oldc;
+ var oldd;
+ var a = 1732584193;
+ var b = -271733879;
+ var c = -1732584194;
+ var d = 271733878;
+
+ for (i = 0; i < x.length; i += 16) {
+ olda = a;
+ oldb = b;
+ oldc = c;
+ oldd = d;
+
+ a = this.md5ff(a, b, c, d, x[i], 7, -680876936);
+ d = this.md5ff(d, a, b, c, x[i + 1], 12, -389564586);
+ c = this.md5ff(c, d, a, b, x[i + 2], 17, 606105819);
+ b = this.md5ff(b, c, d, a, x[i + 3], 22, -1044525330);
+ a = this.md5ff(a, b, c, d, x[i + 4], 7, -176418897);
+ d = this.md5ff(d, a, b, c, x[i + 5], 12, 1200080426);
+ c = this.md5ff(c, d, a, b, x[i + 6], 17, -1473231341);
+ b = this.md5ff(b, c, d, a, x[i + 7], 22, -45705983);
+ a = this.md5ff(a, b, c, d, x[i + 8], 7, 1770035416);
+ d = this.md5ff(d, a, b, c, x[i + 9], 12, -1958414417);
+ c = this.md5ff(c, d, a, b, x[i + 10], 17, -42063);
+ b = this.md5ff(b, c, d, a, x[i + 11], 22, -1990404162);
+ a = this.md5ff(a, b, c, d, x[i + 12], 7, 1804603682);
+ d = this.md5ff(d, a, b, c, x[i + 13], 12, -40341101);
+ c = this.md5ff(c, d, a, b, x[i + 14], 17, -1502002290);
+ b = this.md5ff(b, c, d, a, x[i + 15], 22, 1236535329);
+
+ a = this.md5gg(a, b, c, d, x[i + 1], 5, -165796510);
+ d = this.md5gg(d, a, b, c, x[i + 6], 9, -1069501632);
+ c = this.md5gg(c, d, a, b, x[i + 11], 14, 643717713);
+ b = this.md5gg(b, c, d, a, x[i], 20, -373897302);
+ a = this.md5gg(a, b, c, d, x[i + 5], 5, -701558691);
+ d = this.md5gg(d, a, b, c, x[i + 10], 9, 38016083);
+ c = this.md5gg(c, d, a, b, x[i + 15], 14, -660478335);
+ b = this.md5gg(b, c, d, a, x[i + 4], 20, -405537848);
+ a = this.md5gg(a, b, c, d, x[i + 9], 5, 568446438);
+ d = this.md5gg(d, a, b, c, x[i + 14], 9, -1019803690);
+ c = this.md5gg(c, d, a, b, x[i + 3], 14, -187363961);
+ b = this.md5gg(b, c, d, a, x[i + 8], 20, 1163531501);
+ a = this.md5gg(a, b, c, d, x[i + 13], 5, -1444681467);
+ d = this.md5gg(d, a, b, c, x[i + 2], 9, -51403784);
+ c = this.md5gg(c, d, a, b, x[i + 7], 14, 1735328473);
+ b = this.md5gg(b, c, d, a, x[i + 12], 20, -1926607734);
+
+ a = this.md5hh(a, b, c, d, x[i + 5], 4, -378558);
+ d = this.md5hh(d, a, b, c, x[i + 8], 11, -2022574463);
+ c = this.md5hh(c, d, a, b, x[i + 11], 16, 1839030562);
+ b = this.md5hh(b, c, d, a, x[i + 14], 23, -35309556);
+ a = this.md5hh(a, b, c, d, x[i + 1], 4, -1530992060);
+ d = this.md5hh(d, a, b, c, x[i + 4], 11, 1272893353);
+ c = this.md5hh(c, d, a, b, x[i + 7], 16, -155497632);
+ b = this.md5hh(b, c, d, a, x[i + 10], 23, -1094730640);
+ a = this.md5hh(a, b, c, d, x[i + 13], 4, 681279174);
+ d = this.md5hh(d, a, b, c, x[i], 11, -358537222);
+ c = this.md5hh(c, d, a, b, x[i + 3], 16, -722521979);
+ b = this.md5hh(b, c, d, a, x[i + 6], 23, 76029189);
+ a = this.md5hh(a, b, c, d, x[i + 9], 4, -640364487);
+ d = this.md5hh(d, a, b, c, x[i + 12], 11, -421815835);
+ c = this.md5hh(c, d, a, b, x[i + 15], 16, 530742520);
+ b = this.md5hh(b, c, d, a, x[i + 2], 23, -995338651);
+
+ a = this.md5ii(a, b, c, d, x[i], 6, -198630844);
+ d = this.md5ii(d, a, b, c, x[i + 7], 10, 1126891415);
+ c = this.md5ii(c, d, a, b, x[i + 14], 15, -1416354905);
+ b = this.md5ii(b, c, d, a, x[i + 5], 21, -57434055);
+ a = this.md5ii(a, b, c, d, x[i + 12], 6, 1700485571);
+ d = this.md5ii(d, a, b, c, x[i + 3], 10, -1894986606);
+ c = this.md5ii(c, d, a, b, x[i + 10], 15, -1051523);
+ b = this.md5ii(b, c, d, a, x[i + 1], 21, -2054922799);
+ a = this.md5ii(a, b, c, d, x[i + 8], 6, 1873313359);
+ d = this.md5ii(d, a, b, c, x[i + 15], 10, -30611744);
+ c = this.md5ii(c, d, a, b, x[i + 6], 15, -1560198380);
+ b = this.md5ii(b, c, d, a, x[i + 13], 21, 1309151649);
+ a = this.md5ii(a, b, c, d, x[i + 4], 6, -145523070);
+ d = this.md5ii(d, a, b, c, x[i + 11], 10, -1120210379);
+ c = this.md5ii(c, d, a, b, x[i + 2], 15, 718787259);
+ b = this.md5ii(b, c, d, a, x[i + 9], 21, -343485551);
+
+ a = this.safeAdd(a, olda);
+ b = this.safeAdd(b, oldb);
+ c = this.safeAdd(c, oldc);
+ d = this.safeAdd(d, oldd);
+ }
+ return [a, b, c, d];
+ },
+ binl2rstr(input) {
+ var i;
+ var output = '';
+ var length32 = input.length * 32;
+ for (i = 0; i < length32; i += 8) {
+ output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xff);
+ }
+ return output;
+ },
+ rstr2binl(input) {
+ var i;
+ var output = [];
+ output[(input.length >> 2) - 1] = undefined;
+ for (i = 0; i < output.length; i += 1) {
+ output[i] = 0;
+ }
+ var length8 = input.length * 8;
+ for (i = 0; i < length8; i += 8) {
+ output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << (i % 32);
+ }
+ return output;
+ },
+ rstrMD5(s) {
+ return this.binl2rstr(this.binlMD5(this.rstr2binl(s), s.length * 8));
+ },
+ rstrHMACMD5(key, data) {
+ var i;
+ var bkey = this.rstr2binl(key);
+ var ipad = [];
+ var opad = [];
+ var hash;
+ ipad[15] = opad[15] = undefined;
+ if (bkey.length > 16) {
+ bkey = this.binlMD5(bkey, key.length * 8);
+ }
+ for (i = 0; i < 16; i += 1) {
+ ipad[i] = bkey[i] ^ 0x36363636;
+ opad[i] = bkey[i] ^ 0x5c5c5c5c;
+ }
+ hash = this.binlMD5(ipad.concat(this.rstr2binl(data)), 512 + data.length * 8);
+ return this.binl2rstr(this.binlMD5(opad.concat(hash), 512 + 128));
+ },
+ rstr2hex(input) {
+ var hexTab = '0123456789abcdef';
+ var output = '';
+ var x;
+ var i;
+ for (i = 0; i < input.length; i += 1) {
+ x = input.charCodeAt(i);
+ output += hexTab.charAt((x >>> 4) & 0x0f) + hexTab.charAt(x & 0x0f);
+ }
+ return output;
+ },
+ str2rstrUTF8(input) {
+ return unescape(encodeURIComponent(input));
+ },
+ rawMD5(s) {
+ return this.rstrMD5(this.str2rstrUTF8(s));
+ },
+ hexMD5(s) {
+ return this.rstr2hex(this.rawMD5(s));
+ },
+ rawHMACMD5(k, d) {
+ return this.rstrHMACMD5(this.str2rstrUTF8(k), str2rstrUTF8(d));
+ },
+ hexHMACMD5(k, d) {
+ return this.rstr2hex(this.rawHMACMD5(k, d));
+ },
+
+ md5(string, key, raw) {
+ if (!key) {
+ if (!raw) {
+ return this.hexMD5(string);
+ }
+ return this.rawMD5(string);
+ }
+ if (!raw) {
+ return this.hexHMACMD5(key, string);
+ }
+ return this.rawHMACMD5(key, string);
+ },
+ /**
+ * 得到md5加密后的sig参数
+ * @param {Object} requestParam 接口参数
+ * @param {String} sk签名字符串
+ * @param {String} featrue 方法名
+ * @return 返回加密后的sig参数
+ */
+ getSig(requestParam, sk, feature, mode) {
+ var sig = null;
+ var requestArr = [];
+ Object.keys(requestParam).sort().forEach(function(key){
+ requestArr.push(key + '=' + requestParam[key]);
+ });
+ if (feature == 'search') {
+ sig = '/ws/place/v1/search?' + requestArr.join('&') + sk;
+ }
+ if (feature == 'suggest') {
+ sig = '/ws/place/v1/suggestion?' + requestArr.join('&') + sk;
+ }
+ if (feature == 'reverseGeocoder') {
+ sig = '/ws/geocoder/v1/?' + requestArr.join('&') + sk;
+ }
+ if (feature == 'geocoder') {
+ sig = '/ws/geocoder/v1/?' + requestArr.join('&') + sk;
+ }
+ if (feature == 'getCityList') {
+ sig = '/ws/district/v1/list?' + requestArr.join('&') + sk;
+ }
+ if (feature == 'getDistrictByCityId') {
+ sig = '/ws/district/v1/getchildren?' + requestArr.join('&') + sk;
+ }
+ if (feature == 'calculateDistance') {
+ sig = '/ws/distance/v1/?' + requestArr.join('&') + sk;
+ }
+ if (feature == 'direction') {
+ sig = '/ws/direction/v1/' + mode + '?' + requestArr.join('&') + sk;
+ }
+ sig = this.md5(sig);
+ return sig;
+ },
+ /**
+ * 得到终点query字符串
+ * @param {Array|String} 检索数据
+ */
+ location2query(data) {
+ if (typeof data == 'string') {
+ return data;
+ }
+ var query = '';
+ for (var i = 0; i < data.length; i++) {
+ var d = data[i];
+ if (!!query) {
+ query += ';';
+ }
+ if (d.location) {
+ query = query + d.location.lat + ',' + d.location.lng;
+ }
+ if (d.latitude && d.longitude) {
+ query = query + d.latitude + ',' + d.longitude;
+ }
+ }
+ return query;
+ },
+
+ /**
+ * 计算角度
+ */
+ rad(d) {
+ return d * Math.PI / 180.0;
+ },
+ /**
+ * 处理终点location数组
+ * @return 返回终点数组
+ */
+ getEndLocation(location){
+ var to = location.split(';');
+ var endLocation = [];
+ for (var i = 0; i < to.length; i++) {
+ endLocation.push({
+ lat: parseFloat(to[i].split(',')[0]),
+ lng: parseFloat(to[i].split(',')[1])
+ })
+ }
+ return endLocation;
+ },
+
+ /**
+ * 计算两点间直线距离
+ * @param a 表示纬度差
+ * @param b 表示经度差
+ * @return 返回的是距离,单位m
+ */
+ getDistance(latFrom, lngFrom, latTo, lngTo) {
+ var radLatFrom = this.rad(latFrom);
+ var radLatTo = this.rad(latTo);
+ var a = radLatFrom - radLatTo;
+ var b = this.rad(lngFrom) - this.rad(lngTo);
+ var distance = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLatFrom) * Math.cos(radLatTo) * Math.pow(Math.sin(b / 2), 2)));
+ distance = distance * EARTH_RADIUS;
+ distance = Math.round(distance * 10000) / 10000;
+ return parseFloat(distance.toFixed(0));
+ },
+ /**
+ * 使用微信接口进行定位
+ */
+ getWXLocation(success, fail, complete) {
+ wx.getLocation({
+ type: 'gcj02',
+ success: success,
+ fail: fail,
+ complete: complete
+ });
+ },
+
+ /**
+ * 获取location参数
+ */
+ getLocationParam(location) {
+ if (typeof location == 'string') {
+ var locationArr = location.split(',');
+ if (locationArr.length === 2) {
+ location = {
+ latitude: location.split(',')[0],
+ longitude: location.split(',')[1]
+ };
+ } else {
+ location = {};
+ }
+ }
+ return location;
+ },
+
+ /**
+ * 回调函数默认处理
+ */
+ polyfillParam(param) {
+ param.success = param.success || function () { };
+ param.fail = param.fail || function () { };
+ param.complete = param.complete || function () { };
+ },
+
+ /**
+ * 验证param对应的key值是否为空
+ *
+ * @param {Object} param 接口参数
+ * @param {String} key 对应参数的key
+ */
+ checkParamKeyEmpty(param, key) {
+ if (!param[key]) {
+ var errconf = this.buildErrorConfig(ERROR_CONF.PARAM_ERR, ERROR_CONF.PARAM_ERR_MSG + key +'参数格式有误');
+ param.fail(errconf);
+ param.complete(errconf);
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * 验证参数中是否存在检索词keyword
+ *
+ * @param {Object} param 接口参数
+ */
+ checkKeyword(param){
+ return !this.checkParamKeyEmpty(param, 'keyword');
+ },
+
+ /**
+ * 验证location值
+ *
+ * @param {Object} param 接口参数
+ */
+ checkLocation(param) {
+ var location = this.getLocationParam(param.location);
+ if (!location || !location.latitude || !location.longitude) {
+ var errconf = this.buildErrorConfig(ERROR_CONF.PARAM_ERR, ERROR_CONF.PARAM_ERR_MSG + ' location参数格式有误');
+ param.fail(errconf);
+ param.complete(errconf);
+ return false;
+ }
+ return true;
+ },
+
+ /**
+ * 构造错误数据结构
+ * @param {Number} errCode 错误码
+ * @param {Number} errMsg 错误描述
+ */
+ buildErrorConfig(errCode, errMsg) {
+ return {
+ status: errCode,
+ message: errMsg
+ };
+ },
+
+ /**
+ *
+ * 数据处理函数
+ * 根据传入参数不同处理不同数据
+ * @param {String} feature 功能名称
+ * search 地点搜索
+ * suggest关键词提示
+ * reverseGeocoder逆地址解析
+ * geocoder地址解析
+ * getCityList获取城市列表:父集
+ * getDistrictByCityId获取区县列表:子集
+ * calculateDistance距离计算
+ * @param {Object} param 接口参数
+ * @param {Object} data 数据
+ */
+ handleData(param,data,feature){
+ if (feature == 'search') {
+ var searchResult = data.data;
+ var searchSimplify = [];
+ for (var i = 0; i < searchResult.length; i++) {
+ searchSimplify.push({
+ id: searchResult[i].id || null,
+ title: searchResult[i].title || null,
+ latitude: searchResult[i].location && searchResult[i].location.lat || null,
+ longitude: searchResult[i].location && searchResult[i].location.lng || null,
+ address: searchResult[i].address || null,
+ category: searchResult[i].category || null,
+ tel: searchResult[i].tel || null,
+ adcode: searchResult[i].ad_info && searchResult[i].ad_info.adcode || null,
+ city: searchResult[i].ad_info && searchResult[i].ad_info.city || null,
+ district: searchResult[i].ad_info && searchResult[i].ad_info.district || null,
+ province: searchResult[i].ad_info && searchResult[i].ad_info.province || null
+ })
+ }
+ param.success(data, {
+ searchResult: searchResult,
+ searchSimplify: searchSimplify
+ })
+ } else if (feature == 'suggest') {
+ var suggestResult = data.data;
+ var suggestSimplify = [];
+ for (var i = 0; i < suggestResult.length; i++) {
+ suggestSimplify.push({
+ adcode: suggestResult[i].adcode || null,
+ address: suggestResult[i].address || null,
+ category: suggestResult[i].category || null,
+ city: suggestResult[i].city || null,
+ district: suggestResult[i].district || null,
+ id: suggestResult[i].id || null,
+ latitude: suggestResult[i].location && suggestResult[i].location.lat || null,
+ longitude: suggestResult[i].location && suggestResult[i].location.lng || null,
+ province: suggestResult[i].province || null,
+ title: suggestResult[i].title || null,
+ type: suggestResult[i].type || null
+ })
+ }
+ param.success(data, {
+ suggestResult: suggestResult,
+ suggestSimplify: suggestSimplify
+ })
+ } else if (feature == 'reverseGeocoder') {
+ var reverseGeocoderResult = data.result;
+ var reverseGeocoderSimplify = {
+ address: reverseGeocoderResult.address || null,
+ latitude: reverseGeocoderResult.location && reverseGeocoderResult.location.lat || null,
+ longitude: reverseGeocoderResult.location && reverseGeocoderResult.location.lng || null,
+ adcode: reverseGeocoderResult.ad_info && reverseGeocoderResult.ad_info.adcode || null,
+ city: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.city || null,
+ district: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.district || null,
+ nation: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.nation || null,
+ province: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.province || null,
+ street: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.street || null,
+ street_number: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.street_number || null,
+ recommend: reverseGeocoderResult.formatted_addresses && reverseGeocoderResult.formatted_addresses.recommend || null,
+ rough: reverseGeocoderResult.formatted_addresses && reverseGeocoderResult.formatted_addresses.rough || null
+ };
+ if (reverseGeocoderResult.pois) {//判断是否返回周边poi
+ var pois = reverseGeocoderResult.pois;
+ var poisSimplify = [];
+ for (var i = 0;i < pois.length;i++) {
+ poisSimplify.push({
+ id: pois[i].id || null,
+ title: pois[i].title || null,
+ latitude: pois[i].location && pois[i].location.lat || null,
+ longitude: pois[i].location && pois[i].location.lng || null,
+ address: pois[i].address || null,
+ category: pois[i].category || null,
+ adcode: pois[i].ad_info && pois[i].ad_info.adcode || null,
+ city: pois[i].ad_info && pois[i].ad_info.city || null,
+ district: pois[i].ad_info && pois[i].ad_info.district || null,
+ province: pois[i].ad_info && pois[i].ad_info.province || null
+ })
+ }
+ param.success(data,{
+ reverseGeocoderResult: reverseGeocoderResult,
+ reverseGeocoderSimplify: reverseGeocoderSimplify,
+ pois: pois,
+ poisSimplify: poisSimplify
+ })
+ } else {
+ param.success(data, {
+ reverseGeocoderResult: reverseGeocoderResult,
+ reverseGeocoderSimplify: reverseGeocoderSimplify
+ })
+ }
+ } else if (feature == 'geocoder') {
+ var geocoderResult = data.result;
+ var geocoderSimplify = {
+ title: geocoderResult.title || null,
+ latitude: geocoderResult.location && geocoderResult.location.lat || null,
+ longitude: geocoderResult.location && geocoderResult.location.lng || null,
+ adcode: geocoderResult.ad_info && geocoderResult.ad_info.adcode || null,
+ province: geocoderResult.address_components && geocoderResult.address_components.province || null,
+ city: geocoderResult.address_components && geocoderResult.address_components.city || null,
+ district: geocoderResult.address_components && geocoderResult.address_components.district || null,
+ street: geocoderResult.address_components && geocoderResult.address_components.street || null,
+ street_number: geocoderResult.address_components && geocoderResult.address_components.street_number || null,
+ level: geocoderResult.level || null
+ };
+ param.success(data,{
+ geocoderResult: geocoderResult,
+ geocoderSimplify: geocoderSimplify
+ });
+ } else if (feature == 'getCityList') {
+ var provinceResult = data.result[0];
+ var cityResult = data.result[1];
+ var districtResult = data.result[2];
+ param.success(data,{
+ provinceResult: provinceResult,
+ cityResult: cityResult,
+ districtResult: districtResult
+ });
+ } else if (feature == 'getDistrictByCityId') {
+ var districtByCity = data.result[0];
+ param.success(data, districtByCity);
+ } else if (feature == 'calculateDistance') {
+ var calculateDistanceResult = data.result.elements;
+ var distance = [];
+ for (var i = 0; i < calculateDistanceResult.length; i++){
+ distance.push(calculateDistanceResult[i].distance);
+ }
+ param.success(data, {
+ calculateDistanceResult: calculateDistanceResult,
+ distance: distance
+ });
+ } else if (feature == 'direction') {
+ var direction = data.result.routes;
+ param.success(data,direction);
+ } else {
+ param.success(data);
+ }
+ },
+
+ /**
+ * 构造微信请求参数,公共属性处理
+ *
+ * @param {Object} param 接口参数
+ * @param {Object} param 配置项
+ * @param {String} feature 方法名
+ */
+ buildWxRequestConfig(param, options, feature) {
+ var that = this;
+ options.header = { "content-type": "application/json" };
+ options.method = 'GET';
+ options.success = function (res) {
+ var data = res.data;
+ if (data.status === 0) {
+ that.handleData(param, data, feature);
+ } else {
+ param.fail(data);
+ }
+ };
+ options.fail = function (res) {
+ res.statusCode = ERROR_CONF.WX_ERR_CODE;
+ param.fail(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
+ };
+ options.complete = function (res) {
+ var statusCode = +res.statusCode;
+ switch(statusCode) {
+ case ERROR_CONF.WX_ERR_CODE: {
+ param.complete(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
+ break;
+ }
+ case ERROR_CONF.WX_OK_CODE: {
+ var data = res.data;
+ if (data.status === 0) {
+ param.complete(data);
+ } else {
+ param.complete(that.buildErrorConfig(data.status, data.message));
+ }
+ break;
+ }
+ default:{
+ param.complete(that.buildErrorConfig(ERROR_CONF.SYSTEM_ERR, ERROR_CONF.SYSTEM_ERR_MSG));
+ }
+
+ }
+ };
+ return options;
+ },
+
+ /**
+ * 处理用户参数是否传入坐标进行不同的处理
+ */
+ locationProcess(param, locationsuccess, locationfail, locationcomplete) {
+ var that = this;
+ locationfail = locationfail || function (res) {
+ res.statusCode = ERROR_CONF.WX_ERR_CODE;
+ param.fail(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
+ };
+ locationcomplete = locationcomplete || function (res) {
+ if (res.statusCode == ERROR_CONF.WX_ERR_CODE) {
+ param.complete(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
+ }
+ };
+ if (!param.location) {
+ that.getWXLocation(locationsuccess, locationfail, locationcomplete);
+ } else if (that.checkLocation(param)) {
+ var location = Utils.getLocationParam(param.location);
+ locationsuccess(location);
+ }
+ }
+};
+
+
+class QQMapWX {
+
+ /**
+ * 构造函数
+ *
+ * @param {Object} options 接口参数,key 为必选参数
+ */
+ constructor(options) {
+ if (!options.key) {
+ throw Error('key值不能为空');
+ }
+ this.key = options.key;
+ };
+
+ /**
+ * POI周边检索
+ *
+ * @param {Object} options 接口参数对象
+ *
+ * 参数对象结构可以参考
+ * @see http://lbs.qq.com/webservice_v1/guide-search.html
+ */
+ search(options) {
+ var that = this;
+ options = options || {};
+
+ Utils.polyfillParam(options);
+
+ if (!Utils.checkKeyword(options)) {
+ return;
+ }
+
+ var requestParam = {
+ keyword: options.keyword,
+ orderby: options.orderby || '_distance',
+ page_size: options.page_size || 10,
+ page_index: options.page_index || 1,
+ output: 'json',
+ key: that.key
+ };
+
+ if (options.address_format) {
+ requestParam.address_format = options.address_format;
+ }
+
+ if (options.filter) {
+ requestParam.filter = options.filter;
+ }
+
+ var distance = options.distance || "1000";
+ var auto_extend = options.auto_extend || 1;
+ var region = null;
+ var rectangle = null;
+
+ //判断城市限定参数
+ if (options.region) {
+ region = options.region;
+ }
+
+ //矩形限定坐标(暂时只支持字符串格式)
+ if (options.rectangle) {
+ rectangle = options.rectangle;
+ }
+
+ var locationsuccess = function (result) {
+ if (region && !rectangle) {
+ //城市限定参数拼接
+ requestParam.boundary = "region(" + region + "," + auto_extend + "," + result.latitude + "," + result.longitude + ")";
+ if (options.sig) {
+ requestParam.sig = Utils.getSig(requestParam, options.sig, 'search');
+ }
+ } else if (rectangle && !region) {
+ //矩形搜索
+ requestParam.boundary = "rectangle(" + rectangle + ")";
+ if (options.sig) {
+ requestParam.sig = Utils.getSig(requestParam, options.sig, 'search');
+ }
+ } else {
+ requestParam.boundary = "nearby(" + result.latitude + "," + result.longitude + "," + distance + "," + auto_extend + ")";
+ if (options.sig) {
+ requestParam.sig = Utils.getSig(requestParam, options.sig, 'search');
+ }
+ }
+ wx.request(Utils.buildWxRequestConfig(options, {
+ url: URL_SEARCH,
+ data: requestParam
+ }, 'search'));
+ };
+ Utils.locationProcess(options, locationsuccess);
+ };
+
+ /**
+ * sug模糊检索
+ *
+ * @param {Object} options 接口参数对象
+ *
+ * 参数对象结构可以参考
+ * http://lbs.qq.com/webservice_v1/guide-suggestion.html
+ */
+ getSuggestion(options) {
+ var that = this;
+ options = options || {};
+ Utils.polyfillParam(options);
+
+ if (!Utils.checkKeyword(options)) {
+ return;
+ }
+
+ var requestParam = {
+ keyword: options.keyword,
+ region: options.region || '全国',
+ region_fix: options.region_fix || 0,
+ policy: options.policy || 0,
+ page_size: options.page_size || 10,//控制显示条数
+ page_index: options.page_index || 1,//控制页数
+ get_subpois : options.get_subpois || 0,//返回子地点
+ output: 'json',
+ key: that.key
+ };
+ //长地址
+ if (options.address_format) {
+ requestParam.address_format = options.address_format;
+ }
+ //过滤
+ if (options.filter) {
+ requestParam.filter = options.filter;
+ }
+ //排序
+ if (options.location) {
+ var locationsuccess = function (result) {
+ requestParam.location = result.latitude + ',' + result.longitude;
+ if (options.sig) {
+ requestParam.sig = Utils.getSig(requestParam, options.sig, 'suggest');
+ }
+ wx.request(Utils.buildWxRequestConfig(options, {
+ url: URL_SUGGESTION,
+ data: requestParam
+ }, "suggest"));
+ };
+ Utils.locationProcess(options, locationsuccess);
+ } else {
+ if (options.sig) {
+ requestParam.sig = Utils.getSig(requestParam, options.sig, 'suggest');
+ }
+ wx.request(Utils.buildWxRequestConfig(options, {
+ url: URL_SUGGESTION,
+ data: requestParam
+ }, "suggest"));
+ }
+ };
+
+ /**
+ * 逆地址解析
+ *
+ * @param {Object} options 接口参数对象
+ *
+ * 请求参数结构可以参考
+ * http://lbs.qq.com/webservice_v1/guide-gcoder.html
+ */
+ reverseGeocoder(options) {
+ var that = this;
+ options = options || {};
+ Utils.polyfillParam(options);
+ var requestParam = {
+ coord_type: options.coord_type || 5,
+ get_poi: options.get_poi || 0,
+ output: 'json',
+ key: that.key
+ };
+ if (options.poi_options) {
+ requestParam.poi_options = options.poi_options
+ }
+
+ var locationsuccess = function (result) {
+ requestParam.location = result.latitude + ',' + result.longitude;
+ if (options.sig) {
+ requestParam.sig = Utils.getSig(requestParam, options.sig, 'reverseGeocoder');
+ }
+ wx.request(Utils.buildWxRequestConfig(options, {
+ url: URL_GET_GEOCODER,
+ data: requestParam
+ }, 'reverseGeocoder'));
+ };
+ Utils.locationProcess(options, locationsuccess);
+ };
+
+ /**
+ * 地址解析
+ *
+ * @param {Object} options 接口参数对象
+ *
+ * 请求参数结构可以参考
+ * http://lbs.qq.com/webservice_v1/guide-geocoder.html
+ */
+ geocoder(options) {
+ var that = this;
+ options = options || {};
+ Utils.polyfillParam(options);
+
+ if (Utils.checkParamKeyEmpty(options, 'address')) {
+ return;
+ }
+
+ var requestParam = {
+ address: options.address,
+ output: 'json',
+ key: that.key
+ };
+
+ //城市限定
+ if (options.region) {
+ requestParam.region = options.region;
+ }
+
+ if (options.sig) {
+ requestParam.sig = Utils.getSig(requestParam, options.sig, 'geocoder');
+ }
+
+ wx.request(Utils.buildWxRequestConfig(options, {
+ url: URL_GET_GEOCODER,
+ data: requestParam
+ },'geocoder'));
+ };
+
+
+ /**
+ * 获取城市列表
+ *
+ * @param {Object} options 接口参数对象
+ *
+ * 请求参数结构可以参考
+ * http://lbs.qq.com/webservice_v1/guide-region.html
+ */
+ getCityList(options) {
+ var that = this;
+ options = options || {};
+ Utils.polyfillParam(options);
+ var requestParam = {
+ output: 'json',
+ key: that.key
+ };
+
+ if (options.sig) {
+ requestParam.sig = Utils.getSig(requestParam, options.sig, 'getCityList');
+ }
+
+ wx.request(Utils.buildWxRequestConfig(options, {
+ url: URL_CITY_LIST,
+ data: requestParam
+ },'getCityList'));
+ };
+
+ /**
+ * 获取对应城市ID的区县列表
+ *
+ * @param {Object} options 接口参数对象
+ *
+ * 请求参数结构可以参考
+ * http://lbs.qq.com/webservice_v1/guide-region.html
+ */
+ getDistrictByCityId(options) {
+ var that = this;
+ options = options || {};
+ Utils.polyfillParam(options);
+
+ if (Utils.checkParamKeyEmpty(options, 'id')) {
+ return;
+ }
+
+ var requestParam = {
+ id: options.id || '',
+ output: 'json',
+ key: that.key
+ };
+
+ if (options.sig) {
+ requestParam.sig = Utils.getSig(requestParam, options.sig, 'getDistrictByCityId');
+ }
+
+ wx.request(Utils.buildWxRequestConfig(options, {
+ url: URL_AREA_LIST,
+ data: requestParam
+ },'getDistrictByCityId'));
+ };
+
+ /**
+ * 用于单起点到多终点的路线距离(非直线距离)计算:
+ * 支持两种距离计算方式:步行和驾车。
+ * 起点到终点最大限制直线距离10公里。
+ *
+ * 新增直线距离计算。
+ *
+ * @param {Object} options 接口参数对象
+ *
+ * 请求参数结构可以参考
+ * http://lbs.qq.com/webservice_v1/guide-distance.html
+ */
+ calculateDistance(options) {
+ var that = this;
+ options = options || {};
+ Utils.polyfillParam(options);
+
+ if (Utils.checkParamKeyEmpty(options, 'to')) {
+ return;
+ }
+
+ var requestParam = {
+ mode: options.mode || 'walking',
+ to: Utils.location2query(options.to),
+ output: 'json',
+ key: that.key
+ };
+
+ if (options.from) {
+ options.location = options.from;
+ }
+
+ //计算直线距离
+ if(requestParam.mode == 'straight'){
+ var locationsuccess = function (result) {
+ var locationTo = Utils.getEndLocation(requestParam.to);//处理终点坐标
+ var data = {
+ message:"query ok",
+ result:{
+ elements:[]
+ },
+ status:0
+ };
+ for (var i = 0; i < locationTo.length; i++) {
+ data.result.elements.push({//将坐标存入
+ distance: Utils.getDistance(result.latitude, result.longitude, locationTo[i].lat, locationTo[i].lng),
+ duration:0,
+ from:{
+ lat: result.latitude,
+ lng:result.longitude
+ },
+ to:{
+ lat: locationTo[i].lat,
+ lng: locationTo[i].lng
+ }
+ });
+ }
+ var calculateResult = data.result.elements;
+ var distanceResult = [];
+ for (var i = 0; i < calculateResult.length; i++) {
+ distanceResult.push(calculateResult[i].distance);
+ }
+ return options.success(data,{
+ calculateResult: calculateResult,
+ distanceResult: distanceResult
+ });
+ };
+
+ Utils.locationProcess(options, locationsuccess);
+ } else {
+ var locationsuccess = function (result) {
+ requestParam.from = result.latitude + ',' + result.longitude;
+ if (options.sig) {
+ requestParam.sig = Utils.getSig(requestParam, options.sig, 'calculateDistance');
+ }
+ wx.request(Utils.buildWxRequestConfig(options, {
+ url: URL_DISTANCE,
+ data: requestParam
+ },'calculateDistance'));
+ };
+
+ Utils.locationProcess(options, locationsuccess);
+ }
+ };
+
+ /**
+ * 路线规划:
+ *
+ * @param {Object} options 接口参数对象
+ *
+ * 请求参数结构可以参考
+ * https://lbs.qq.com/webservice_v1/guide-road.html
+ */
+ direction(options) {
+ var that = this;
+ options = options || {};
+ Utils.polyfillParam(options);
+
+ if (Utils.checkParamKeyEmpty(options, 'to')) {
+ return;
+ }
+
+ var requestParam = {
+ output: 'json',
+ key: that.key
+ };
+
+ //to格式处理
+ if (typeof options.to == 'string') {
+ requestParam.to = options.to;
+ } else {
+ requestParam.to = options.to.latitude + ',' + options.to.longitude;
+ }
+ //初始化局部请求域名
+ var SET_URL_DIRECTION = null;
+ //设置默认mode属性
+ options.mode = options.mode || MODE.driving;
+
+ //设置请求域名
+ SET_URL_DIRECTION = URL_DIRECTION + options.mode;
+
+ if (options.from) {
+ options.location = options.from;
+ }
+
+ if (options.mode == MODE.driving) {
+ if (options.from_poi) {
+ requestParam.from_poi = options.from_poi;
+ }
+ if (options.heading) {
+ requestParam.heading = options.heading;
+ }
+ if (options.speed) {
+ requestParam.speed = options.speed;
+ }
+ if (options.accuracy) {
+ requestParam.accuracy = options.accuracy;
+ }
+ if (options.road_type) {
+ requestParam.road_type = options.road_type;
+ }
+ if (options.to_poi) {
+ requestParam.to_poi = options.to_poi;
+ }
+ if (options.from_track) {
+ requestParam.from_track = options.from_track;
+ }
+ if (options.waypoints) {
+ requestParam.waypoints = options.waypoints;
+ }
+ if (options.policy) {
+ requestParam.policy = options.policy;
+ }
+ if (options.plate_number) {
+ requestParam.plate_number = options.plate_number;
+ }
+ }
+
+ if (options.mode == MODE.transit) {
+ if (options.departure_time) {
+ requestParam.departure_time = options.departure_time;
+ }
+ if (options.policy) {
+ requestParam.policy = options.policy;
+ }
+ }
+
+ var locationsuccess = function (result) {
+ requestParam.from = result.latitude + ',' + result.longitude;
+ if (options.sig) {
+ requestParam.sig = Utils.getSig(requestParam, options.sig, 'direction',options.mode);
+ }
+ wx.request(Utils.buildWxRequestConfig(options, {
+ url: SET_URL_DIRECTION,
+ data: requestParam
+ }, 'direction'));
+ };
+
+ Utils.locationProcess(options, locationsuccess);
+ }
+};
+
+module.exports = QQMapWX;
\ No newline at end of file
diff --git a/js_sdk/qqmap-wx-jssdk/qqmap-wx-jssdk.min.js b/js_sdk/qqmap-wx-jssdk/qqmap-wx-jssdk.min.js
new file mode 100644
index 0000000..8fa1477
--- /dev/null
+++ b/js_sdk/qqmap-wx-jssdk/qqmap-wx-jssdk.min.js
@@ -0,0 +1 @@
+var ERROR_CONF = { KEY_ERR: 311, KEY_ERR_MSG: 'key格式错误', PARAM_ERR: 310, PARAM_ERR_MSG: '请求参数信息有误', SYSTEM_ERR: 600, SYSTEM_ERR_MSG: '系统错误', WX_ERR_CODE: 1000, WX_OK_CODE: 200 }; var BASE_URL = 'https://apis.map.qq.com/ws/'; var URL_SEARCH = BASE_URL + 'place/v1/search'; var URL_SUGGESTION = BASE_URL + 'place/v1/suggestion'; var URL_GET_GEOCODER = BASE_URL + 'geocoder/v1/'; var URL_CITY_LIST = BASE_URL + 'district/v1/list'; var URL_AREA_LIST = BASE_URL + 'district/v1/getchildren'; var URL_DISTANCE = BASE_URL + 'distance/v1/'; var URL_DIRECTION = BASE_URL + 'direction/v1/'; var MODE = { driving: 'driving', transit: 'transit' }; var EARTH_RADIUS = 6378136.49; var Utils = { safeAdd(x, y) { var lsw = (x & 0xffff) + (y & 0xffff); var msw = (x >> 16) + (y >> 16) + (lsw >> 16); return (msw << 16) | (lsw & 0xffff) }, bitRotateLeft(num, cnt) { return (num << cnt) | (num >>> (32 - cnt)) }, md5cmn(q, a, b, x, s, t) { return this.safeAdd(this.bitRotateLeft(this.safeAdd(this.safeAdd(a, q), this.safeAdd(x, t)), s), b) }, md5ff(a, b, c, d, x, s, t) { return this.md5cmn((b & c) | (~b & d), a, b, x, s, t) }, md5gg(a, b, c, d, x, s, t) { return this.md5cmn((b & d) | (c & ~d), a, b, x, s, t) }, md5hh(a, b, c, d, x, s, t) { return this.md5cmn(b ^ c ^ d, a, b, x, s, t) }, md5ii(a, b, c, d, x, s, t) { return this.md5cmn(c ^ (b | ~d), a, b, x, s, t) }, binlMD5(x, len) { x[len >> 5] |= 0x80 << (len % 32); x[((len + 64) >>> 9 << 4) + 14] = len; var i; var olda; var oldb; var oldc; var oldd; var a = 1732584193; var b = -271733879; var c = -1732584194; var d = 271733878; for (i = 0; i < x.length; i += 16) { olda = a; oldb = b; oldc = c; oldd = d; a = this.md5ff(a, b, c, d, x[i], 7, -680876936); d = this.md5ff(d, a, b, c, x[i + 1], 12, -389564586); c = this.md5ff(c, d, a, b, x[i + 2], 17, 606105819); b = this.md5ff(b, c, d, a, x[i + 3], 22, -1044525330); a = this.md5ff(a, b, c, d, x[i + 4], 7, -176418897); d = this.md5ff(d, a, b, c, x[i + 5], 12, 1200080426); c = this.md5ff(c, d, a, b, x[i + 6], 17, -1473231341); b = this.md5ff(b, c, d, a, x[i + 7], 22, -45705983); a = this.md5ff(a, b, c, d, x[i + 8], 7, 1770035416); d = this.md5ff(d, a, b, c, x[i + 9], 12, -1958414417); c = this.md5ff(c, d, a, b, x[i + 10], 17, -42063); b = this.md5ff(b, c, d, a, x[i + 11], 22, -1990404162); a = this.md5ff(a, b, c, d, x[i + 12], 7, 1804603682); d = this.md5ff(d, a, b, c, x[i + 13], 12, -40341101); c = this.md5ff(c, d, a, b, x[i + 14], 17, -1502002290); b = this.md5ff(b, c, d, a, x[i + 15], 22, 1236535329); a = this.md5gg(a, b, c, d, x[i + 1], 5, -165796510); d = this.md5gg(d, a, b, c, x[i + 6], 9, -1069501632); c = this.md5gg(c, d, a, b, x[i + 11], 14, 643717713); b = this.md5gg(b, c, d, a, x[i], 20, -373897302); a = this.md5gg(a, b, c, d, x[i + 5], 5, -701558691); d = this.md5gg(d, a, b, c, x[i + 10], 9, 38016083); c = this.md5gg(c, d, a, b, x[i + 15], 14, -660478335); b = this.md5gg(b, c, d, a, x[i + 4], 20, -405537848); a = this.md5gg(a, b, c, d, x[i + 9], 5, 568446438); d = this.md5gg(d, a, b, c, x[i + 14], 9, -1019803690); c = this.md5gg(c, d, a, b, x[i + 3], 14, -187363961); b = this.md5gg(b, c, d, a, x[i + 8], 20, 1163531501); a = this.md5gg(a, b, c, d, x[i + 13], 5, -1444681467); d = this.md5gg(d, a, b, c, x[i + 2], 9, -51403784); c = this.md5gg(c, d, a, b, x[i + 7], 14, 1735328473); b = this.md5gg(b, c, d, a, x[i + 12], 20, -1926607734); a = this.md5hh(a, b, c, d, x[i + 5], 4, -378558); d = this.md5hh(d, a, b, c, x[i + 8], 11, -2022574463); c = this.md5hh(c, d, a, b, x[i + 11], 16, 1839030562); b = this.md5hh(b, c, d, a, x[i + 14], 23, -35309556); a = this.md5hh(a, b, c, d, x[i + 1], 4, -1530992060); d = this.md5hh(d, a, b, c, x[i + 4], 11, 1272893353); c = this.md5hh(c, d, a, b, x[i + 7], 16, -155497632); b = this.md5hh(b, c, d, a, x[i + 10], 23, -1094730640); a = this.md5hh(a, b, c, d, x[i + 13], 4, 681279174); d = this.md5hh(d, a, b, c, x[i], 11, -358537222); c = this.md5hh(c, d, a, b, x[i + 3], 16, -722521979); b = this.md5hh(b, c, d, a, x[i + 6], 23, 76029189); a = this.md5hh(a, b, c, d, x[i + 9], 4, -640364487); d = this.md5hh(d, a, b, c, x[i + 12], 11, -421815835); c = this.md5hh(c, d, a, b, x[i + 15], 16, 530742520); b = this.md5hh(b, c, d, a, x[i + 2], 23, -995338651); a = this.md5ii(a, b, c, d, x[i], 6, -198630844); d = this.md5ii(d, a, b, c, x[i + 7], 10, 1126891415); c = this.md5ii(c, d, a, b, x[i + 14], 15, -1416354905); b = this.md5ii(b, c, d, a, x[i + 5], 21, -57434055); a = this.md5ii(a, b, c, d, x[i + 12], 6, 1700485571); d = this.md5ii(d, a, b, c, x[i + 3], 10, -1894986606); c = this.md5ii(c, d, a, b, x[i + 10], 15, -1051523); b = this.md5ii(b, c, d, a, x[i + 1], 21, -2054922799); a = this.md5ii(a, b, c, d, x[i + 8], 6, 1873313359); d = this.md5ii(d, a, b, c, x[i + 15], 10, -30611744); c = this.md5ii(c, d, a, b, x[i + 6], 15, -1560198380); b = this.md5ii(b, c, d, a, x[i + 13], 21, 1309151649); a = this.md5ii(a, b, c, d, x[i + 4], 6, -145523070); d = this.md5ii(d, a, b, c, x[i + 11], 10, -1120210379); c = this.md5ii(c, d, a, b, x[i + 2], 15, 718787259); b = this.md5ii(b, c, d, a, x[i + 9], 21, -343485551); a = this.safeAdd(a, olda); b = this.safeAdd(b, oldb); c = this.safeAdd(c, oldc); d = this.safeAdd(d, oldd) } return [a, b, c, d] }, binl2rstr(input) { var i; var output = ''; var length32 = input.length * 32; for (i = 0; i < length32; i += 8) { output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xff) } return output }, rstr2binl(input) { var i; var output = []; output[(input.length >> 2) - 1] = undefined; for (i = 0; i < output.length; i += 1) { output[i] = 0 } var length8 = input.length * 8; for (i = 0; i < length8; i += 8) { output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << (i % 32) } return output }, rstrMD5(s) { return this.binl2rstr(this.binlMD5(this.rstr2binl(s), s.length * 8)) }, rstrHMACMD5(key, data) { var i; var bkey = this.rstr2binl(key); var ipad = []; var opad = []; var hash; ipad[15] = opad[15] = undefined; if (bkey.length > 16) { bkey = this.binlMD5(bkey, key.length * 8) } for (i = 0; i < 16; i += 1) { ipad[i] = bkey[i] ^ 0x36363636; opad[i] = bkey[i] ^ 0x5c5c5c5c } hash = this.binlMD5(ipad.concat(this.rstr2binl(data)), 512 + data.length * 8); return this.binl2rstr(this.binlMD5(opad.concat(hash), 512 + 128)) }, rstr2hex(input) { var hexTab = '0123456789abcdef'; var output = ''; var x; var i; for (i = 0; i < input.length; i += 1) { x = input.charCodeAt(i); output += hexTab.charAt((x >>> 4) & 0x0f) + hexTab.charAt(x & 0x0f) } return output }, str2rstrUTF8(input) { return unescape(encodeURIComponent(input)) }, rawMD5(s) { return this.rstrMD5(this.str2rstrUTF8(s)) }, hexMD5(s) { return this.rstr2hex(this.rawMD5(s)) }, rawHMACMD5(k, d) { return this.rstrHMACMD5(this.str2rstrUTF8(k), str2rstrUTF8(d)) }, hexHMACMD5(k, d) { return this.rstr2hex(this.rawHMACMD5(k, d)) }, md5(string, key, raw) { if (!key) { if (!raw) { return this.hexMD5(string) } return this.rawMD5(string) } if (!raw) { return this.hexHMACMD5(key, string) } return this.rawHMACMD5(key, string) }, getSig(requestParam, sk, feature, mode) { var sig = null; var requestArr = []; Object.keys(requestParam).sort().forEach(function (key) { requestArr.push(key + '=' + requestParam[key]) }); if (feature == 'search') { sig = '/ws/place/v1/search?' + requestArr.join('&') + sk } if (feature == 'suggest') { sig = '/ws/place/v1/suggestion?' + requestArr.join('&') + sk } if (feature == 'reverseGeocoder') { sig = '/ws/geocoder/v1/?' + requestArr.join('&') + sk } if (feature == 'geocoder') { sig = '/ws/geocoder/v1/?' + requestArr.join('&') + sk } if (feature == 'getCityList') { sig = '/ws/district/v1/list?' + requestArr.join('&') + sk } if (feature == 'getDistrictByCityId') { sig = '/ws/district/v1/getchildren?' + requestArr.join('&') + sk } if (feature == 'calculateDistance') { sig = '/ws/distance/v1/?' + requestArr.join('&') + sk } if (feature == 'direction') { sig = '/ws/direction/v1/' + mode + '?' + requestArr.join('&') + sk } sig = this.md5(sig); return sig }, location2query(data) { if (typeof data == 'string') { return data } var query = ''; for (var i = 0; i < data.length; i++) { var d = data[i]; if (!!query) { query += ';' } if (d.location) { query = query + d.location.lat + ',' + d.location.lng } if (d.latitude && d.longitude) { query = query + d.latitude + ',' + d.longitude } } return query }, rad(d) { return d * Math.PI / 180.0 }, getEndLocation(location) { var to = location.split(';'); var endLocation = []; for (var i = 0; i < to.length; i++) { endLocation.push({ lat: parseFloat(to[i].split(',')[0]), lng: parseFloat(to[i].split(',')[1]) }) } return endLocation }, getDistance(latFrom, lngFrom, latTo, lngTo) { var radLatFrom = this.rad(latFrom); var radLatTo = this.rad(latTo); var a = radLatFrom - radLatTo; var b = this.rad(lngFrom) - this.rad(lngTo); var distance = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLatFrom) * Math.cos(radLatTo) * Math.pow(Math.sin(b / 2), 2))); distance = distance * EARTH_RADIUS; distance = Math.round(distance * 10000) / 10000; return parseFloat(distance.toFixed(0)) }, getWXLocation(success, fail, complete) { wx.getLocation({ type: 'gcj02', success: success, fail: fail, complete: complete }) }, getLocationParam(location) { if (typeof location == 'string') { var locationArr = location.split(','); if (locationArr.length === 2) { location = { latitude: location.split(',')[0], longitude: location.split(',')[1] } } else { location = {} } } return location }, polyfillParam(param) { param.success = param.success || function () { }; param.fail = param.fail || function () { }; param.complete = param.complete || function () { } }, checkParamKeyEmpty(param, key) { if (!param[key]) { var errconf = this.buildErrorConfig(ERROR_CONF.PARAM_ERR, ERROR_CONF.PARAM_ERR_MSG + key + '参数格式有误'); param.fail(errconf); param.complete(errconf); return true } return false }, checkKeyword(param) { return !this.checkParamKeyEmpty(param, 'keyword') }, checkLocation(param) { var location = this.getLocationParam(param.location); if (!location || !location.latitude || !location.longitude) { var errconf = this.buildErrorConfig(ERROR_CONF.PARAM_ERR, ERROR_CONF.PARAM_ERR_MSG + ' location参数格式有误'); param.fail(errconf); param.complete(errconf); return false } return true }, buildErrorConfig(errCode, errMsg) { return { status: errCode, message: errMsg } }, handleData(param, data, feature) { if (feature == 'search') { var searchResult = data.data; var searchSimplify = []; for (var i = 0; i < searchResult.length; i++) { searchSimplify.push({ id: searchResult[i].id || null, title: searchResult[i].title || null, latitude: searchResult[i].location && searchResult[i].location.lat || null, longitude: searchResult[i].location && searchResult[i].location.lng || null, address: searchResult[i].address || null, category: searchResult[i].category || null, tel: searchResult[i].tel || null, adcode: searchResult[i].ad_info && searchResult[i].ad_info.adcode || null, city: searchResult[i].ad_info && searchResult[i].ad_info.city || null, district: searchResult[i].ad_info && searchResult[i].ad_info.district || null, province: searchResult[i].ad_info && searchResult[i].ad_info.province || null }) } param.success(data, { searchResult: searchResult, searchSimplify: searchSimplify }) } else if (feature == 'suggest') { var suggestResult = data.data; var suggestSimplify = []; for (var i = 0; i < suggestResult.length; i++) { suggestSimplify.push({ adcode: suggestResult[i].adcode || null, address: suggestResult[i].address || null, category: suggestResult[i].category || null, city: suggestResult[i].city || null, district: suggestResult[i].district || null, id: suggestResult[i].id || null, latitude: suggestResult[i].location && suggestResult[i].location.lat || null, longitude: suggestResult[i].location && suggestResult[i].location.lng || null, province: suggestResult[i].province || null, title: suggestResult[i].title || null, type: suggestResult[i].type || null }) } param.success(data, { suggestResult: suggestResult, suggestSimplify: suggestSimplify }) } else if (feature == 'reverseGeocoder') { var reverseGeocoderResult = data.result; var reverseGeocoderSimplify = { address: reverseGeocoderResult.address || null, latitude: reverseGeocoderResult.location && reverseGeocoderResult.location.lat || null, longitude: reverseGeocoderResult.location && reverseGeocoderResult.location.lng || null, adcode: reverseGeocoderResult.ad_info && reverseGeocoderResult.ad_info.adcode || null, city: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.city || null, district: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.district || null, nation: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.nation || null, province: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.province || null, street: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.street || null, street_number: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.street_number || null, recommend: reverseGeocoderResult.formatted_addresses && reverseGeocoderResult.formatted_addresses.recommend || null, rough: reverseGeocoderResult.formatted_addresses && reverseGeocoderResult.formatted_addresses.rough || null }; if (reverseGeocoderResult.pois) { var pois = reverseGeocoderResult.pois; var poisSimplify = []; for (var i = 0; i < pois.length; i++) { poisSimplify.push({ id: pois[i].id || null, title: pois[i].title || null, latitude: pois[i].location && pois[i].location.lat || null, longitude: pois[i].location && pois[i].location.lng || null, address: pois[i].address || null, category: pois[i].category || null, adcode: pois[i].ad_info && pois[i].ad_info.adcode || null, city: pois[i].ad_info && pois[i].ad_info.city || null, district: pois[i].ad_info && pois[i].ad_info.district || null, province: pois[i].ad_info && pois[i].ad_info.province || null }) } param.success(data, { reverseGeocoderResult: reverseGeocoderResult, reverseGeocoderSimplify: reverseGeocoderSimplify, pois: pois, poisSimplify: poisSimplify }) } else { param.success(data, { reverseGeocoderResult: reverseGeocoderResult, reverseGeocoderSimplify: reverseGeocoderSimplify }) } } else if (feature == 'geocoder') { var geocoderResult = data.result; var geocoderSimplify = { title: geocoderResult.title || null, latitude: geocoderResult.location && geocoderResult.location.lat || null, longitude: geocoderResult.location && geocoderResult.location.lng || null, adcode: geocoderResult.ad_info && geocoderResult.ad_info.adcode || null, province: geocoderResult.address_components && geocoderResult.address_components.province || null, city: geocoderResult.address_components && geocoderResult.address_components.city || null, district: geocoderResult.address_components && geocoderResult.address_components.district || null, street: geocoderResult.address_components && geocoderResult.address_components.street || null, street_number: geocoderResult.address_components && geocoderResult.address_components.street_number || null, level: geocoderResult.level || null }; param.success(data, { geocoderResult: geocoderResult, geocoderSimplify: geocoderSimplify }) } else if (feature == 'getCityList') { var provinceResult = data.result[0]; var cityResult = data.result[1]; var districtResult = data.result[2]; param.success(data, { provinceResult: provinceResult, cityResult: cityResult, districtResult: districtResult }) } else if (feature == 'getDistrictByCityId') { var districtByCity = data.result[0]; param.success(data, districtByCity) } else if (feature == 'calculateDistance') { var calculateDistanceResult = data.result.elements; var distance = []; for (var i = 0; i < calculateDistanceResult.length; i++) { distance.push(calculateDistanceResult[i].distance) } param.success(data, { calculateDistanceResult: calculateDistanceResult, distance: distance }) } else if (feature == 'direction') { var direction = data.result.routes; param.success(data, direction) } else { param.success(data) } }, buildWxRequestConfig(param, options, feature) { var that = this; options.header = { "content-type": "application/json" }; options.method = 'GET'; options.success = function (res) { var data = res.data; if (data.status === 0) { that.handleData(param, data, feature) } else { param.fail(data) } }; options.fail = function (res) { res.statusCode = ERROR_CONF.WX_ERR_CODE; param.fail(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg)) }; options.complete = function (res) { var statusCode = +res.statusCode; switch (statusCode) { case ERROR_CONF.WX_ERR_CODE: { param.complete(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg)); break } case ERROR_CONF.WX_OK_CODE: { var data = res.data; if (data.status === 0) { param.complete(data) } else { param.complete(that.buildErrorConfig(data.status, data.message)) } break } default: { param.complete(that.buildErrorConfig(ERROR_CONF.SYSTEM_ERR, ERROR_CONF.SYSTEM_ERR_MSG)) } } }; return options }, locationProcess(param, locationsuccess, locationfail, locationcomplete) { var that = this; locationfail = locationfail || function (res) { res.statusCode = ERROR_CONF.WX_ERR_CODE; param.fail(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg)) }; locationcomplete = locationcomplete || function (res) { if (res.statusCode == ERROR_CONF.WX_ERR_CODE) { param.complete(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg)) } }; if (!param.location) { that.getWXLocation(locationsuccess, locationfail, locationcomplete) } else if (that.checkLocation(param)) { var location = Utils.getLocationParam(param.location); locationsuccess(location) } } }; class QQMapWX { constructor(options) { if (!options.key) { throw Error('key值不能为空') } this.key = options.key }; search(options) { var that = this; options = options || {}; Utils.polyfillParam(options); if (!Utils.checkKeyword(options)) { return } var requestParam = { keyword: options.keyword, orderby: options.orderby || '_distance', page_size: options.page_size || 10, page_index: options.page_index || 1, output: 'json', key: that.key }; if (options.address_format) { requestParam.address_format = options.address_format } if (options.filter) { requestParam.filter = options.filter } var distance = options.distance || "1000"; var auto_extend = options.auto_extend || 1; var region = null; var rectangle = null; if (options.region) { region = options.region } if (options.rectangle) { rectangle = options.rectangle } var locationsuccess = function (result) { if (region && !rectangle) { requestParam.boundary = "region(" + region + "," + auto_extend + "," + result.latitude + "," + result.longitude + ")"; if (options.sig) { requestParam.sig = Utils.getSig(requestParam, options.sig, 'search') } } else if (rectangle && !region) { requestParam.boundary = "rectangle(" + rectangle + ")"; if (options.sig) { requestParam.sig = Utils.getSig(requestParam, options.sig, 'search') } } else { requestParam.boundary = "nearby(" + result.latitude + "," + result.longitude + "," + distance + "," + auto_extend + ")"; if (options.sig) { requestParam.sig = Utils.getSig(requestParam, options.sig, 'search') } } wx.request(Utils.buildWxRequestConfig(options, { url: URL_SEARCH, data: requestParam }, 'search')) }; Utils.locationProcess(options, locationsuccess) }; getSuggestion(options) { var that = this; options = options || {}; Utils.polyfillParam(options); if (!Utils.checkKeyword(options)) { return } var requestParam = { keyword: options.keyword, region: options.region || '全国', region_fix: options.region_fix || 0, policy: options.policy || 0, page_size: options.page_size || 10, page_index: options.page_index || 1, get_subpois: options.get_subpois || 0, output: 'json', key: that.key }; if (options.address_format) { requestParam.address_format = options.address_format } if (options.filter) { requestParam.filter = options.filter } if (options.location) { var locationsuccess = function (result) { requestParam.location = result.latitude + ',' + result.longitude; if (options.sig) { requestParam.sig = Utils.getSig(requestParam, options.sig, 'suggest') } wx.request(Utils.buildWxRequestConfig(options, { url: URL_SUGGESTION, data: requestParam }, "suggest")) }; Utils.locationProcess(options, locationsuccess) } else { if (options.sig) { requestParam.sig = Utils.getSig(requestParam, options.sig, 'suggest') } wx.request(Utils.buildWxRequestConfig(options, { url: URL_SUGGESTION, data: requestParam }, "suggest")) } }; reverseGeocoder(options) { var that = this; options = options || {}; Utils.polyfillParam(options); var requestParam = { coord_type: options.coord_type || 5, get_poi: options.get_poi || 0, output: 'json', key: that.key }; if (options.poi_options) { requestParam.poi_options = options.poi_options } var locationsuccess = function (result) { requestParam.location = result.latitude + ',' + result.longitude; if (options.sig) { requestParam.sig = Utils.getSig(requestParam, options.sig, 'reverseGeocoder') } wx.request(Utils.buildWxRequestConfig(options, { url: URL_GET_GEOCODER, data: requestParam }, 'reverseGeocoder')) }; Utils.locationProcess(options, locationsuccess) }; geocoder(options) { var that = this; options = options || {}; Utils.polyfillParam(options); if (Utils.checkParamKeyEmpty(options, 'address')) { return } var requestParam = { address: options.address, output: 'json', key: that.key }; if (options.region) { requestParam.region = options.region } if (options.sig) { requestParam.sig = Utils.getSig(requestParam, options.sig, 'geocoder') } wx.request(Utils.buildWxRequestConfig(options, { url: URL_GET_GEOCODER, data: requestParam }, 'geocoder')) }; getCityList(options) { var that = this; options = options || {}; Utils.polyfillParam(options); var requestParam = { output: 'json', key: that.key }; if (options.sig) { requestParam.sig = Utils.getSig(requestParam, options.sig, 'getCityList') } wx.request(Utils.buildWxRequestConfig(options, { url: URL_CITY_LIST, data: requestParam }, 'getCityList')) }; getDistrictByCityId(options) { var that = this; options = options || {}; Utils.polyfillParam(options); if (Utils.checkParamKeyEmpty(options, 'id')) { return } var requestParam = { id: options.id || '', output: 'json', key: that.key }; if (options.sig) { requestParam.sig = Utils.getSig(requestParam, options.sig, 'getDistrictByCityId') } wx.request(Utils.buildWxRequestConfig(options, { url: URL_AREA_LIST, data: requestParam }, 'getDistrictByCityId')) }; calculateDistance(options) { var that = this; options = options || {}; Utils.polyfillParam(options); if (Utils.checkParamKeyEmpty(options, 'to')) { return } var requestParam = { mode: options.mode || 'walking', to: Utils.location2query(options.to), output: 'json', key: that.key }; if (options.from) { options.location = options.from } if (requestParam.mode == 'straight') { var locationsuccess = function (result) { var locationTo = Utils.getEndLocation(requestParam.to); var data = { message: "query ok", result: { elements: [] }, status: 0 }; for (var i = 0; i < locationTo.length; i++) { data.result.elements.push({ distance: Utils.getDistance(result.latitude, result.longitude, locationTo[i].lat, locationTo[i].lng), duration: 0, from: { lat: result.latitude, lng: result.longitude }, to: { lat: locationTo[i].lat, lng: locationTo[i].lng } }) } var calculateResult = data.result.elements; var distanceResult = []; for (var i = 0; i < calculateResult.length; i++) { distanceResult.push(calculateResult[i].distance) } return options.success(data, { calculateResult: calculateResult, distanceResult: distanceResult }) }; Utils.locationProcess(options, locationsuccess) } else { var locationsuccess = function (result) { requestParam.from = result.latitude + ',' + result.longitude; if (options.sig) { requestParam.sig = Utils.getSig(requestParam, options.sig, 'calculateDistance') } wx.request(Utils.buildWxRequestConfig(options, { url: URL_DISTANCE, data: requestParam }, 'calculateDistance')) }; Utils.locationProcess(options, locationsuccess) } }; direction(options) { var that = this; options = options || {}; Utils.polyfillParam(options); if (Utils.checkParamKeyEmpty(options, 'to')) { return } var requestParam = { output: 'json', key: that.key }; if (typeof options.to == 'string') { requestParam.to = options.to } else { requestParam.to = options.to.latitude + ',' + options.to.longitude } var SET_URL_DIRECTION = null; options.mode = options.mode || MODE.driving; SET_URL_DIRECTION = URL_DIRECTION + options.mode; if (options.from) { options.location = options.from } if (options.mode == MODE.driving) { if (options.from_poi) { requestParam.from_poi = options.from_poi } if (options.heading) { requestParam.heading = options.heading } if (options.speed) { requestParam.speed = options.speed } if (options.accuracy) { requestParam.accuracy = options.accuracy } if (options.road_type) { requestParam.road_type = options.road_type } if (options.to_poi) { requestParam.to_poi = options.to_poi } if (options.from_track) { requestParam.from_track = options.from_track } if (options.waypoints) { requestParam.waypoints = options.waypoints } if (options.policy) { requestParam.policy = options.policy } if (options.plate_number) { requestParam.plate_number = options.plate_number } } if (options.mode == MODE.transit) { if (options.departure_time) { requestParam.departure_time = options.departure_time } if (options.policy) { requestParam.policy = options.policy } } var locationsuccess = function (result) { requestParam.from = result.latitude + ',' + result.longitude; if (options.sig) { requestParam.sig = Utils.getSig(requestParam, options.sig, 'direction', options.mode) } wx.request(Utils.buildWxRequestConfig(options, { url: SET_URL_DIRECTION, data: requestParam }, 'direction')) }; Utils.locationProcess(options, locationsuccess) } }; module.exports = QQMapWX;
\ No newline at end of file
diff --git a/main.js b/main.js
new file mode 100644
index 0000000..c40f895
--- /dev/null
+++ b/main.js
@@ -0,0 +1,23 @@
+import App from './App'
+import uView from '@/uni_modules/uview-ui'
+// #ifndef VUE3
+import Vue from 'vue'
+import './uni.promisify.adaptor'
+Vue.config.productionTip = false
+Vue.use(uView)
+App.mpType = 'app'
+const app = new Vue({
+ ...App
+})
+app.$mount()
+// #endif
+
+// #ifdef VUE3
+import { createSSRApp } from 'vue'
+export function createApp() {
+ const app = createSSRApp(App)
+ return {
+ app
+ }
+}
+// #endif
\ No newline at end of file
diff --git a/manifest.json b/manifest.json
new file mode 100644
index 0000000..2eda26e
--- /dev/null
+++ b/manifest.json
@@ -0,0 +1,78 @@
+{
+ "name" : "yaodongxiao",
+ "appid" : "__UNI__5EE4E0E",
+ "description" : "",
+ "versionName" : "1.0.0",
+ "versionCode" : "100",
+ "transformPx" : false,
+ /* 5+App特有相关 */
+ "app-plus" : {
+ "usingComponents" : true,
+ "nvueStyleCompiler" : "uni-app",
+ "compilerVersion" : 3,
+ "splashscreen" : {
+ "alwaysShowBeforeRender" : true,
+ "waiting" : true,
+ "autoclose" : true,
+ "delay" : 0
+ },
+ /* 模块配置 */
+ "modules" : {},
+ /* 应用发布信息 */
+ "distribute" : {
+ /* android打包配置 */
+ "android" : {
+ "permissions" : [
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ ""
+ ]
+ },
+ /* ios打包配置 */
+ "ios" : {},
+ /* SDK配置 */
+ "sdkConfigs" : {}
+ }
+ },
+ /* 快应用特有相关 */
+ "quickapp" : {},
+ /* 小程序特有相关 */
+ "mp-weixin" : {
+ "appid" : "wx0c1587a63b0b9db5",
+ "setting" : {
+ "urlCheck" : false
+ },
+ "usingComponents" : true,
+ "permission" : {
+ "scope.userLocation" : {
+ "desc" : "定位店铺"
+ }
+ },
+ "requiredPrivateInfos" : [ "chooseLocation", "getLocation" ]
+ },
+ "mp-alipay" : {
+ "usingComponents" : true
+ },
+ "mp-baidu" : {
+ "usingComponents" : true
+ },
+ "mp-toutiao" : {
+ "usingComponents" : true
+ },
+ "uniStatistics" : {
+ "enable" : false
+ },
+ "vueVersion" : "2"
+}
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..23bc844
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,96 @@
+{
+ "name": "yaodongxiao",
+ "version": "1.0.0",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "yaodongxiao",
+ "version": "1.0.0",
+ "license": "ISC",
+ "dependencies": {
+ "dayjs": "^1.11.19",
+ "js-base64": "^3.7.8",
+ "md5": "^2.3.0"
+ }
+ },
+ "node_modules/charenc": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmmirror.com/charenc/-/charenc-0.0.2.tgz",
+ "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/crypt": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmmirror.com/crypt/-/crypt-0.0.2.tgz",
+ "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/dayjs": {
+ "version": "1.11.19",
+ "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.19.tgz",
+ "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw=="
+ },
+ "node_modules/is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+ },
+ "node_modules/js-base64": {
+ "version": "3.7.8",
+ "resolved": "https://registry.npmmirror.com/js-base64/-/js-base64-3.7.8.tgz",
+ "integrity": "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow=="
+ },
+ "node_modules/md5": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmmirror.com/md5/-/md5-2.3.0.tgz",
+ "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==",
+ "dependencies": {
+ "charenc": "0.0.2",
+ "crypt": "0.0.2",
+ "is-buffer": "~1.1.6"
+ }
+ }
+ },
+ "dependencies": {
+ "charenc": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmmirror.com/charenc/-/charenc-0.0.2.tgz",
+ "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA=="
+ },
+ "crypt": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmmirror.com/crypt/-/crypt-0.0.2.tgz",
+ "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow=="
+ },
+ "dayjs": {
+ "version": "1.11.19",
+ "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.19.tgz",
+ "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw=="
+ },
+ "is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+ },
+ "js-base64": {
+ "version": "3.7.8",
+ "resolved": "https://registry.npmmirror.com/js-base64/-/js-base64-3.7.8.tgz",
+ "integrity": "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow=="
+ },
+ "md5": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmmirror.com/md5/-/md5-2.3.0.tgz",
+ "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==",
+ "requires": {
+ "charenc": "0.0.2",
+ "crypt": "0.0.2",
+ "is-buffer": "~1.1.6"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..fb0ebfd
--- /dev/null
+++ b/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "yaodongxiao",
+ "version": "1.0.0",
+ "description": "",
+ "main": "main.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "dayjs": "^1.11.19",
+ "js-base64": "^3.7.8",
+ "md5": "^2.3.0"
+ }
+}
diff --git a/packageUser/address/index.vue b/packageUser/address/index.vue
new file mode 100644
index 0000000..b59a33f
--- /dev/null
+++ b/packageUser/address/index.vue
@@ -0,0 +1,539 @@
+
+
+
+
+
+
+
+ 收货地址管理
+
+ 请填写准确的收货信息
+
+
+
+
+
+ 地址列表
+ 新增地址
+
+ 加载中...
+ 暂无地址
+
+
+
+ {{ item.name }} {{ maskPhone(item.phone) }}
+ 默认
+
+ {{ item.fullAddress || buildFullAddress(item) }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packageUser/chronic/index.vue b/packageUser/chronic/index.vue
new file mode 100644
index 0000000..79afbbc
--- /dev/null
+++ b/packageUser/chronic/index.vue
@@ -0,0 +1,1394 @@
+
+
+
+
+
+
+
+ 慢病信息收集
+
+ 收集三高信息,完善姓名、性别、电话、出生年月日、病史、用药史。
+ 信息仅用于慢病管理与跟进,提交后可在后台查看。
+
+
+
+
+ 基础信息
+
+
+ 连锁
+
+ {{ form.chainName || (merchantLoading ? '加载中...' : '请选择') }}
+
+
+
+ 门店
+
+ {{ form.merchantName || '请选择' }}
+
+
+
+ 姓名
+
+
+
+ 性别
+ {{ form.gender || '请选择' }}
+
+
+ 电话
+
+
+
+ 出生年月日
+ {{ form.birthday || '请选择' }}
+
+
+
+
+
+ 血液信息
+
+
+ 血红蛋白参考值
+
+
+ 血红蛋白数值
+
+
+
+ 是否贫血
+ {{ form.anemiaLevel || '请选择' }}
+
+
+ 是否地贫
+ {{ form.thalassemiaType || '请选择' }}
+
+
+
+
+
+
+ 用药追踪
+ 添加
+
+ 选择药品后填写本次购药时间和购买盒数,系统会按每盒服用天数自动计算预计服用完时间。
+ 暂无用药追踪,可点击添加记录。
+
+
+ 用药追踪{{ index + 1 }}
+ 移除
+
+
+
+ 药品
+
+ {{ item.productName || (trackingProductLoading ? '加载中...' : '请选择') }}
+
+
+
+ 规格粒/片:{{ item.dailyUsageCount || '-' }}
+ 说明书每天使用/次:{{ item.dosagePerTime || '-' }}
+ 每次几粒/片:{{ item.boxUsageDays || '-' }}
+
+
+ 本次购药时间
+ {{ item.purchaseTime || '请选择' }}
+
+
+ 购买盒数
+
+
+
+ 预估服用完时间
+ {{ item.estimatedFinishTime || '-' }}
+
+
+
+
+
+
+ 三高信息
+
+
+
+
+
+
+
+ 高血压参考值
+
+
+ 收缩压
+
+
+
+ 舒张压
+
+
+
+
+
+ 高血糖参考值
+
+
+ 空腹血糖
+
+
+
+ 餐后2小时血糖
+
+
+
+ 糖化血红蛋白(HbA1c)
+
+
+
+
+
+ 高血脂参考值
+
+
+ 总胆固醇(TC)
+
+
+
+ 甘油三酯(TG)
+
+
+
+ 低密度脂蛋白(LDL-C)(“坏”胆固醇)
+
+
+
+ 高密度脂蛋白(HDL-C)(“好”胆固醇)
+
+
+
+
+
+
+ 病史
+
+
+
+
+ 用药史
+
+
+
+ 提交信息
+
+
+
+
+
+
+
+
+
+ 取消
+ 选择门店
+ 确定
+
+
+
+
+
+
+
+
+
+
+ {{ item.text }}
+
+
+
+
+
+
+
+
+ 取消
+ 选择药品
+ 完成
+
+
+
+
+
+
+
+
+ 暂无药品
+
+ {{ item.productName || '-' }}
+ 规格粒/片:{{ item.dailyUsageCount || '-' }}
+ 说明书每天使用/次:{{ item.dosagePerTime || '-' }}
+ 每次几粒/片:{{ item.boxUsageDays || '-' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packageUser/chronic/manage.vue b/packageUser/chronic/manage.vue
new file mode 100644
index 0000000..5059f91
--- /dev/null
+++ b/packageUser/chronic/manage.vue
@@ -0,0 +1,1074 @@
+
+
+
+
+
+
+
+ 慢病信息管理
+
+ 支持按姓名搜索
+
+
+
+
+ 搜索
+
+
+ 搜索
+
+
+
+
+ 信息列表
+ 加载中...
+ 暂无数据
+
+
+
+
+ {{ item.name || '-' }}
+ {{ item.gender || '未知' }} · {{ item.phone || '-' }}
+
+
+ {{ item._expanded ? '收起' : '展开' }}
+
+
+
+ {{ tag }}
+ 无三高
+
+ 连锁:{{ item.chainName || '-' }}
+
+ 出生日期:{{ item.birthday || '-' }}年龄:{{ resolveAge(item.birthday) }}
+ 门店:{{ item.merchantName || '-' }}
+ 病史:{{ item.medicalHistory || '-' }}
+ 用药史:{{ item.medicationHistory || '-' }}
+
+
+ 用药追踪
+ 新增用药追踪
+
+
+
+
+
+ 药品
+ 规格粒/片
+ 说明书每天使用/次
+ 每次几粒/片
+ 本次购药时间
+ 购买盒数
+ 预估服用完时间
+
+
+ {{ tracking.productName || '-' }}
+ {{ formatField(tracking.dailyUsageCount) }}
+ {{ formatField(tracking.dosagePerTime) }}
+ {{ formatField(tracking.boxUsageDays) }}
+ {{ tracking.purchaseTime || '-' }}
+ {{ formatField(tracking.boxCount) }}
+ {{ tracking.estimatedFinishTime || '-' }}
+
+
+
+
+ 暂无用药追踪
+
+ 血红蛋白数值:{{ formatField(item.hemoglobinValue) }}
+ 是否贫血:{{ formatEnumField(item.anemiaLevel, '否') }}
+ 是否地贫:{{ formatEnumField(item.thalassemiaType, '否') }}
+
+ 高血压数值:{{ formatField(item.highBloodPressureValue) }}
+ 收缩压:{{ resolveSystolicPressure(item) }}
+ 舒张压:{{ resolveDiastolicPressure(item) }}
+
+
+ 空腹血糖:{{ formatField(item.fastingBloodGlucose) }}
+ 餐后2小时血糖:{{ formatField(item.postprandial2hBloodGlucose) }}
+ 糖化血红蛋白(HbA1c):{{ formatField(item.hba1c) }}
+
+
+ 总胆固醇(TC):{{ formatField(item.totalCholesterol) }}
+ 甘油三酯(TG):{{ formatField(item.triglyceride) }}
+ 低密度脂蛋白(LDL-C):{{ formatField(item.ldlC) }}
+ 高密度脂蛋白(HDL-C):{{ formatField(item.hdlC) }}
+
+ 提交时间:{{ formatTime(item.createTime || item.createDate || item.createdAt) }}
+
+
+
+
+
+
+
+
+ 取消
+ 新增用药追踪
+ 保存
+
+
+ 按当前慢病记录所属连锁拉取药品,保存后会追加到该记录的用药追踪中。
+
+ 药品
+
+ {{ trackingEditorForm.productName || (trackingProductLoading ? '加载中...' : '请选择') }}
+
+
+
+ 规格粒/片:{{ trackingEditorForm.dailyUsageCount || '-' }}
+ 说明书每天使用/次:{{ trackingEditorForm.dosagePerTime || '-' }}
+ 每次几粒/片:{{ trackingEditorForm.boxUsageDays || '-' }}
+
+
+ 本次购药时间
+ {{ trackingEditorForm.purchaseTime || '请选择' }}
+
+
+ 购买盒数
+
+
+
+ 预估服用完时间
+ {{ trackingEditorForm.estimatedFinishTime || '-' }}
+
+
+
+
+
+
+
+
+ 取消
+ 选择药品
+ 完成
+
+
+
+
+
+
+
+
+ 暂无药品
+
+ {{ item.productName || '-' }}
+ 规格粒/片:{{ item.dailyUsageCount || '-' }}
+ 说明书每天使用/次:{{ item.dosagePerTime || '-' }}
+ 每次几粒/片:{{ item.boxUsageDays || '-' }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packageUser/coupon/index.vue b/packageUser/coupon/index.vue
new file mode 100644
index 0000000..bd521b3
--- /dev/null
+++ b/packageUser/coupon/index.vue
@@ -0,0 +1,299 @@
+
+
+
+
+
+
+
+ 优惠券中心
+
+ 领券大厅与我的优惠券
+
+
+
+
+
+
+ {{ tab.label }}
+
+
+
+
+
+ {{ activeTab === 'lobby' ? '领券大厅' : '我的优惠券' }}
+ 加载中...
+ 暂无优惠券
+
+
+
+
+
+ {{ coupon.title }}
+
+
+ 有效期:{{ coupon.expireType === 1 ? coupon.expireEndDate : `领取后${coupon.expireDay}天过期` }}
+
+
+
+
+ 领取
+
+ {{ coupon.statusText }}
+
+
+
+
+
+
+
+
+
diff --git a/packageUser/points/index.vue b/packageUser/points/index.vue
new file mode 100644
index 0000000..714b2d6
--- /dev/null
+++ b/packageUser/points/index.vue
@@ -0,0 +1,345 @@
+
+
+
+
+
+
+
+ 我的积分
+
+ 查看积分余额和积分明细记录。
+
+
+
+
+
+ {{ summary.currentPoints }}
+ 当前积分
+
+
+ {{ summary.totalEarned }}
+ 累计获得
+
+
+
+
+
+
+
+
+ 积分明细
+ 加载中...
+ 暂无积分记录
+
+
+
+
+ {{ item.title }}
+
+ {{ item.deltaText }}
+
+
+ {{ dayjs(item.date).format('YYYY-MM-DD HH:mm:ss') }}
+ {{ item.remark }}
+
+
+ {{ finished ? '没有更多了' : '上拉加载更多' }}
+
+
+
+
+
+
+
+
diff --git a/packageUser/self-report/create.vue b/packageUser/self-report/create.vue
new file mode 100644
index 0000000..fb4d70f
--- /dev/null
+++ b/packageUser/self-report/create.vue
@@ -0,0 +1,2080 @@
+
+
+
+
+
+ 连锁筛选
+
+
+ {{ selectedChainText }}
+
+
+
+
+
+
+ 门店筛选
+
+
+ {{ selectedMerchantText }}
+
+
+
+
+
+
+ 活动日期
+
+
+ {{ form.date || '请选择活动日期' }}
+
+
+
+
+
+
+ 上班打卡(水印拍照)
+
+
+
+
+
+
+
+
+
+
+
+ {{ (clockPhotos[0] && clockPhotos[0].time) || '--:--' }}
+ {{ (clockPhotos[0] && clockPhotos[0].date) || '--' }}
+
+
+
+
+
+
+
+
+ 门头摆台照
+
+
+
+
+
+ x
+
+
+
+
+
+
+
+
+
+ 销量填写
+
+
+
+
+
+ 新增商品
+
+
+ 请点击“新增商品”逐个添加销量商品
+
+
+
+
+ {{ item.name }}
+ {{ item.groupTitle }}
+
+ 删除
+
+
+
+ 厂家
+ {{ item.makerAbb }}
+
+
+ 编码
+ {{ item.goodsCode || '-' }}
+
+
+
+
+
+
+ {{ item.spec || '规格' }}
+
+
+
+
+
+
+ {{ item.unit || '盒' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 小票上传
+ (必须原图)
+
+
+
+
+
+
+ x
+
+
+
+
+
+
+
+
+ 活动小结
+
+
+
+
+ 下班打卡(水印拍照)
+
+
+
+
+
+
+
+
+
+
+
+ {{ (clockPhotos[1] && clockPhotos[1].time) || '--:--' }}
+ {{ (clockPhotos[1] && clockPhotos[1].date) || '--' }}
+
+
+
+
+
+
+
+
+ 门店评分
+
+
+
+
+
+
+
+
+ 保存草稿
+ {{ submitLoading ? '提交中...' : '提交销量' }}
+
+
+
+
+
+
+ 取消
+ 选择连锁
+ 确定
+
+
+
+
+
+
+
+ {{ item.text }}
+
+
+
+
+
+
+
+
+
+ 取消
+ 选择门店
+ 确定
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.text }}
+
+
+
+
+
+
+
+
+
+ 取消
+ 选择商品
+ 完成
+
+
+
+
+
+
+
+
+
+
+ 暂无可选商品
+ 该分类暂无可选商品
+
+
+
+
+ {{ item.name }}
+ {{ item.makerAbb || '-' }} / {{ item.spec || '规格' }}
+
+
+ {{ isProductSelected(item) ? '已添加' : '添加' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packageUser/self-report/records.vue b/packageUser/self-report/records.vue
new file mode 100644
index 0000000..41216b7
--- /dev/null
+++ b/packageUser/self-report/records.vue
@@ -0,0 +1,833 @@
+
+
+
+
+
+
+
+ {{ tab.label }}
+
+
+
+
+
+ 筛选查询
+
+ {{ chainLabel }}
+ {{ filterForm.startDate || '开始日期' }}
+ {{ filterForm.endDate || '结束日期' }}
+
+
+
+ 查询
+ 重置
+
+
+
+
+ 记录列表
+ 加载中...
+ 暂无记录
+
+
+
+
+ {{ item.merchantName || '-' }}
+ 连锁:{{ item.chainName }}
+
+ {{ statusText(item.status) }}
+
+
+
+ 活动时间
+ {{ item.activityTime || '-' }}
+
+
+
+
+
+
+ 评分
+ {{ item.rating || '-' }}星
+
+
+ 提交时间
+ {{ item.submitTime || '-' }}
+
+
+ 小结:{{ item.summary || '-' }}
+
+ {{ isExpanded(item.id) ? '收起详情' : '展开详情' }}
+
+
+
+
+ 上下班打卡
+
+
+
+ 暂无打卡图片
+
+
+
+ 门头摆台照
+
+
+
+ 暂无门头摆台照
+
+
+
+ 小票上传
+
+
+
+ 暂无小票
+
+
+
+ 销量填写
+
+
+
+ {{ detail.name }}
+ {{ detail.qty }}{{ detail.unit }}
+
+
+ {{ detail.groupTitle }}
+ {{ detail.makerAbb || '-' }}
+ {{ detail.spec || '-' }}
+
+
+
+ 暂无销量数据
+
+
+
+
+
+
+
+ onDateConfirm('start', e)"
+ @cancel="closeDatePicker('start')"
+ @close="closeDatePicker('start')"
+ />
+ onDateConfirm('end', e)"
+ @cancel="closeDatePicker('end')"
+ @close="closeDatePicker('end')"
+ />
+
+
+
+
+
+
+
+
diff --git a/packageUser/travel/index.vue b/packageUser/travel/index.vue
new file mode 100644
index 0000000..eba670b
--- /dev/null
+++ b/packageUser/travel/index.vue
@@ -0,0 +1,893 @@
+
+
+
+
+
+
+
+ 差旅报销
+
+ 填写出差时间、出发地和目的地,上传凭证后进入审核。
+
+
+
+
+ 新增报销
+
+
+ 开始时间
+ {{ travelForm.startDate || '点击选择' }}
+
+
+ 结束时间
+ {{ travelForm.endDate || '点击选择' }}
+
+
+ 出发地
+
+ {{ fromRegionText }}
+
+
+
+ 目的地
+
+ {{ toRegionText }}
+
+
+
+ 交通费
+
+
+
+ 住宿费
+
+
+
+ 其他费用
+
+
+
+ 备注
+
+
+
+
+ 出差凭证
+ {{ images.length ? '已上传,点击继续上传' : '点击上传照片' }}
+
+ 必填
+
+
+
+
+ x
+
+
+ 提交报销
+
+
+
+
+ 历史报销
+ 加载中...
+ 暂无记录
+
+
+
+
+ {{ item.fromText }} → {{ item.toText }}
+ {{ item.start }} 至 {{ item.end }}
+
+
+
+ {{ item.status }}
+
+
+
+
+
+ 交通 ¥{{ item.traffic }} · 住宿 ¥{{ item.hotel }}
+ 其他 ¥{{ item.other }}
+ 备注:{{ item.remark || '-' }}
+ 凭证:
+
+
+
+
+
+ -
+
+
+
+
+
+ onDateConfirm('startDate', e)"
+ @cancel="closeDatePicker('startDate')"
+ @close="closeDatePicker('startDate')"
+ />
+ onDateConfirm('endDate', e)"
+ @cancel="closeDatePicker('endDate')"
+ @close="closeDatePicker('endDate')"
+ />
+
+
+
+
+
+
+
+
+
diff --git a/pages.json b/pages.json
new file mode 100644
index 0000000..dd0c9c3
--- /dev/null
+++ b/pages.json
@@ -0,0 +1,271 @@
+{
+ "pages": [
+ {
+ "path": "pages/index/index",
+ "style": {
+ "navigationBarTitleText": "药动销",
+ "navigationStyle": "custom",
+ "enablePullDownRefresh": true
+ }
+ },
+ {
+ "path": "pages/exchange/index",
+ "style": {
+ "navigationBarTitleText": "药兑换",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/share/index",
+ "style": {
+ "navigationBarTitleText": "药分享",
+ "navigationStyle": "custom",
+ "enablePullDownRefresh": true
+ }
+ },
+ {
+ "path": "pages/share/mine",
+ "style": {
+ "navigationBarTitleText": "我的分享",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/share/detail",
+ "style": {
+ "navigationBarTitleText": "分享详情",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/share/create",
+ "style": {
+ "navigationBarTitleText": "我要分享",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/performance/index",
+ "style": {
+ "navigationBarTitleText": "团队业绩",
+ "navigationStyle": "custom",
+ "enablePullDownRefresh": true
+ }
+ },
+ {
+ "path": "pages/mine/index",
+ "style": {
+ "navigationBarTitleText": "我的",
+ "navigationStyle": "custom",
+ "enablePullDownRefresh": true
+ }
+ },
+ {
+ "path": "pages/mine/profile",
+ "style": {
+ "navigationBarTitleText": "用户资料",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/auth/login",
+ "style": {
+ "navigationBarTitleText": "微信快捷登录",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/task/detail",
+ "style": {
+ "navigationBarTitleText": "任务详情",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/certification/index",
+ "style": {
+ "navigationBarTitleText": "身份认证",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/visit/index",
+ "style": {
+ "navigationBarTitleText": "培训拜访",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/cart/index",
+ "style": {
+ "navigationBarTitleText": "购物车",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/product/detail",
+ "style": {
+ "navigationBarTitleText": "商品详情",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/order/checkout",
+ "style": {
+ "navigationBarTitleText": "订单结算",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/order/list",
+ "style": {
+ "navigationBarTitleText": "订单列表",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/order/detail",
+ "style": {
+ "navigationBarTitleText": "订单详情",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/bonus/index",
+ "style": {
+ "navigationBarTitleText": "记录",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/bonus/withdraw",
+ "style": {
+ "navigationBarTitleText": "提现",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/bonus/withdraw-record",
+ "style": {
+ "navigationBarTitleText": "提现记录",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/audit/index",
+ "style": {
+ "navigationBarTitleText": "药审核",
+ "navigationStyle": "custom"
+ }
+ }
+ ],
+ "subPackages": [
+ {
+ "root": "packageUser",
+ "pages": [
+ {
+ "path": "self-report/create",
+ "style": {
+ "navigationBarTitleText": "自主填报",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "self-report/records",
+ "style": {
+ "navigationBarTitleText": "填报记录",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "coupon/index",
+ "style": {
+ "navigationBarTitleText": "领券中心",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "points/index",
+ "style": {
+ "navigationBarTitleText": "我的积分",
+ "navigationStyle": "custom",
+ "enablePullDownRefresh": true
+ }
+ },
+ {
+ "path": "travel/index",
+ "style": {
+ "navigationBarTitleText": "差旅报销",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "address/index",
+ "style": {
+ "navigationBarTitleText": "收货地址管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "chronic/index",
+ "style": {
+ "navigationBarTitleText": "慢病管理",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "chronic/manage",
+ "style": {
+ "navigationBarTitleText": "慢病信息管理",
+ "navigationStyle": "custom"
+ }
+ }
+ ]
+ }
+ ],
+ "tabBar": {
+ "color": "#6b7a90",
+ "selectedColor": "#2fc6a7",
+ "borderStyle": "black",
+ "backgroundColor": "#ffffff",
+ "list": [
+ {
+ "pagePath": "pages/index/index",
+ "iconPath": "static/tabbar/home.png",
+ "selectedIconPath": "static/tabbar/home-active.png",
+ "text": "药动销"
+ },
+// {
+// "pagePath": "pages/exchange/index",
+// "iconPath": "static/tabbar/switch.png",
+// "selectedIconPath": "static/tabbar/switch-active.png",
+// "text": "药兑换"
+// },
+ {
+ "pagePath": "pages/share/index",
+ "iconPath": "static/tabbar/share.png",
+ "selectedIconPath": "static/tabbar/share-active.png",
+ "text": "药分享"
+ },
+ {
+ "pagePath": "pages/performance/index",
+ "iconPath": "static/tabbar/group.png",
+ "selectedIconPath": "static/tabbar/group-active.png",
+ "text": "团队业绩"
+ },
+ {
+ "pagePath": "pages/mine/index",
+ "iconPath": "static/tabbar/user.png",
+ "selectedIconPath": "static/tabbar/user-active.png",
+ "text": "我的"
+ }
+ ]
+ },
+ "globalStyle": {
+ "navigationBarTextStyle": "black",
+ "navigationBarTitleText": "药小二",
+ "navigationBarBackgroundColor": "#ffffff",
+ "backgroundColor": "#f4f7fb"
+ },
+ "uniIdRouter": {}
+}
diff --git a/pages/audit/components/AuditMovementItem.vue b/pages/audit/components/AuditMovementItem.vue
new file mode 100644
index 0000000..4107a20
--- /dev/null
+++ b/pages/audit/components/AuditMovementItem.vue
@@ -0,0 +1,344 @@
+
+
+
+
+ 任务:{{ item.taskTitle }}
+ 连锁:{{ item.chainName }}
+ 门店:{{ item.merchantName }}
+ 提交人:{{ item.owner }} · {{ item.submitTime }}
+
+
+
+
+
+
+
+
+ 通过
+
+
+ 驳回
+
+
+ 抄送
+
+
+ 查看详情
+
+
+
+
+ 抄送
+
+
+ 查看详情
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/audit/components/AuditSelfReportItem.vue b/pages/audit/components/AuditSelfReportItem.vue
new file mode 100644
index 0000000..826e660
--- /dev/null
+++ b/pages/audit/components/AuditSelfReportItem.vue
@@ -0,0 +1,1413 @@
+
+
+
+
+ 连锁:{{ currentRecord.chainName }}
+
+ 门店:{{ currentRecord.merchantName || '-' }}
+ 修改
+
+
+ 活动日期:{{ currentRecord.date || item.date || '-' }}
+ 修改
+
+
+ 评分:{{ currentRecord.rating || '-' }} 星
+ 提交人:{{ currentRecord.owner || item.owner }}
+ 小结:{{ currentRecord.summary || '-' }}
+ 驳回原因:{{ currentRecord.rejectReason }}
+
+ {{ detailExpanded ? '收起详情' : '展开详情' }}
+
+
+
+
+ 上下班打卡
+
+
+
+ 暂无打卡图片
+
+
+
+ 门头摆台照
+
+
+
+ 暂无门头摆台照
+
+
+
+ 小票上传
+
+
+
+ 暂无小票
+
+
+
+
+ 销量填写
+ 修改销量
+
+
+
+
+ {{ detail.name }}
+ 销量:{{ detail.qty }}{{ detail.unit }}
+
+
+
+ 分组
+ {{ detail.groupTitle }}
+
+
+ 厂家
+ {{ detail.makerAbb || '-' }}
+
+
+ 规格
+ {{ detail.spec || '-' }}
+
+
+
+
+ 暂无销量数据
+
+
+
+
+
+ 通过
+
+
+ 驳回
+
+
+ 修改销量
+
+
+
+
+
+
+
+
+
+
+ 取消
+ 修改门店
+ {{ merchantEditSaving ? '保存中...' : '保存' }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ chain.text }}
+
+
+
+
+ {{ merchant.text }}
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+ 选择商品
+ 完成
+
+
+
+
+
+
+
+
+
+
+ 暂无可选商品
+ 该分类暂无商品
+
+
+
+ {{ product.name || '-' }}
+ {{ product.makerAbb || '-' }} / {{ product.spec || '规格' }}
+ 编码:{{ product.goodsCode || '-' }}
+
+
+ {{ isProductSelected(product) ? '已添加' : '添加' }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/audit/components/AuditShareItem.vue b/pages/audit/components/AuditShareItem.vue
new file mode 100644
index 0000000..f7044ba
--- /dev/null
+++ b/pages/audit/components/AuditShareItem.vue
@@ -0,0 +1,1930 @@
+
+
+
+
+
+ 门店:{{ currentItem.merchantName }}
+
+
+
+
+
+ 提交人:{{ currentItem.owner || item.owner }} · {{ currentItem.date || item.date }}
+ 留言:{{ currentItem.remark || '-' }}
+
+ {{ detailExpanded ? '收起详情' : '展开详情' }}
+
+
+
+
+ 小票查看
+
+
+
+
+
+
+
+
+ {{ detail.goodsName || detail.name || detail.title || '商品' }}
+
+
+
+ 数量
+ x {{ detail.num }}
+
+
+ 厂家
+ {{ resolveManufacturer(detail) }}
+
+
+ 编号
+ {{ resolveCode(detail) }}
+
+
+ 规格
+ {{ resolveSpec(detail) }}
+
+
+ 金额
+ {{ formatMoney(resolveAmount(detail)) }}
+
+
+
+
+
+
+
+
+ 暂无明细
+
+
+
+
+
+ 通过
+
+
+ 驳回
+
+
+ 修改
+
+
+ 抄送
+
+
+
+
+ 抄送
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+ 选择连锁和门店
+ 确定
+
+
+
+
+
+
+
+
+
+ {{ item.text }}
+
+
+ {{ item.text }}
+
+
+
+
+
+
+
+
+ 取消
+ 选择药品
+ 完成
+
+
+
+
+
+
+
+
+ 暂无药品
+
+ {{ product.goodsName || '-' }}
+ 厂家:{{ product.makerAbb || '-' }}
+ 规格:{{ product.spec || '-' }}
+ 编码:{{ product.goodsCode || '-' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/audit/components/AuditStoreVisitItem.vue b/pages/audit/components/AuditStoreVisitItem.vue
new file mode 100644
index 0000000..ae8d3ed
--- /dev/null
+++ b/pages/audit/components/AuditStoreVisitItem.vue
@@ -0,0 +1,348 @@
+
+
+
+
+ 任务:{{ item.taskTitle }}
+ 连锁:{{ item.chainName }}
+ 门店:{{ item.merchantName }}
+ 提交人:{{ item.owner }} · {{ item.submitTime }}
+
+
+
+
+
+
+
+
+ 通过
+
+
+ 驳回
+
+
+ 抄送
+
+
+ 查看详情
+
+
+
+
+ 抄送
+
+
+ 查看详情
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/audit/components/record.scss b/pages/audit/components/record.scss
new file mode 100644
index 0000000..de7bd30
--- /dev/null
+++ b/pages/audit/components/record.scss
@@ -0,0 +1,220 @@
+.record {
+ //border-bottom: 15rpx solid #f5f5f5;
+ //border-radius: 999rpx;
+ padding: 12rpx 20rpx;
+ border-radius: 40rpx;
+ background-color: white;
+ margin: 15rpx 0;
+}
+
+.record-title {
+ font-size: 30rpx;
+ color: var(--text-main);
+}
+
+.record-meta {
+ color: var(--text-sub);
+ font-size: 24rpx;
+ margin-top: 6rpx;
+}
+
+.record-media {
+ margin-top: 10rpx;
+ display: flex;
+ flex-direction: column;
+ gap: 10rpx;
+}
+
+.record-extra {
+ margin-top: 10rpx;
+ display: flex;
+ flex-direction: column;
+ gap: 12rpx;
+}
+
+.record-section {
+ margin-top: 10rpx;
+}
+
+.record-section-title {
+ font-size: 26rpx;
+ color: var(--text-main);
+ font-weight: 600;
+ margin-bottom: 6rpx;
+}
+
+.clock-list {
+ display: flex;
+ gap: 12rpx;
+ flex-wrap: wrap;
+}
+
+.clock-item {
+ width: 200rpx;
+}
+
+.clock-image {
+ width: 200rpx;
+ height: 160rpx;
+ border-radius: 12rpx;
+ overflow: hidden;
+}
+
+.clock-meta {
+ margin-top: 6rpx;
+ font-size: 22rpx;
+ color: var(--text-sub);
+}
+
+.clock-label {
+ margin-right: 6rpx;
+ color: var(--text-main);
+ font-weight: 600;
+}
+
+.sales-audit-group {
+ background: #f7f9fc;
+ border-radius: 12rpx;
+ padding: 12rpx;
+ margin-bottom: 8rpx;
+}
+
+.sales-audit-group:last-of-type {
+ margin-bottom: 0;
+}
+
+.sales-audit-title {
+ font-size: 24rpx;
+ color: var(--text-main);
+ font-weight: 600;
+ margin-bottom: 6rpx;
+}
+
+.sales-audit-item {
+ display: flex;
+ justify-content: space-between;
+ font-size: 22rpx;
+ color: var(--text-main);
+ padding: 4rpx 0;
+}
+
+.sales-audit-item.muted {
+ color: var(--text-sub);
+ opacity: 0.7;
+}
+
+.sales-audit-spec {
+ color: var(--text-sub);
+}
+
+.sales-audit-qty {
+ color: #2563eb;
+}
+
+.summary-text {
+ background: #f7f9fc;
+ border-radius: 12rpx;
+ padding: 12rpx;
+ font-size: 24rpx;
+ color: var(--text-main);
+}
+
+.record-detail {
+ margin-top: 10rpx;
+}
+
+.detail-list {
+ display: flex;
+ flex-direction: column;
+ gap: 6rpx;
+}
+
+.detail-item {
+ display: flex;
+ flex-direction: column;
+ gap: 6rpx;
+ font-size: 24rpx;
+ padding: 8rpx 10rpx;
+ border-radius: 10rpx;
+ background: #f7f9fc;
+ margin-bottom: 10rpx;
+}
+
+.detail-head {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 12rpx;
+}
+
+.detail-name {
+ color: var(--text-main);
+ font-weight: 600;
+ flex: 1;
+ word-break: break-all;
+}
+
+.detail-qty {
+ color: var(--text-sub);
+ white-space: nowrap;
+}
+
+.detail-grid {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8rpx 16rpx;
+}
+
+.detail-kv {
+ display: flex;
+ gap: 6rpx;
+ min-width: 45%;
+}
+
+.detail-label {
+ color: var(--text-sub);
+ white-space: nowrap;
+}
+
+.detail-value {
+ color: var(--text-main);
+ flex: 1;
+ word-break: break-all;
+}
+
+.detail-empty {
+ color: var(--text-sub);
+ font-size: 24rpx;
+}
+
+.media-images {
+ display: flex;
+ gap: 10rpx;
+ flex-wrap: wrap;
+}
+
+.media-image {
+ width: 160rpx;
+ height: 160rpx;
+ border-radius: 12rpx;
+ overflow: hidden;
+}
+
+.media-video video {
+ width: 100%;
+ border-radius: 12rpx;
+ overflow: hidden;
+}
+
+.record-actions {
+ display: flex;
+ gap: 10rpx;
+ flex-wrap: wrap;
+ margin-top: 12rpx;
+}
+
+.danger-chip {
+ color: #f5576c;
+ border-color: #f6c5d0;
+ background: #fff2f5;
+}
diff --git a/pages/audit/index.vue b/pages/audit/index.vue
new file mode 100644
index 0000000..3acdee9
--- /dev/null
+++ b/pages/audit/index.vue
@@ -0,0 +1,1387 @@
+
+
+
+
+
+
+
+ 药审核
+
+
+
+
+
+
+
+ {{ tab.label }}
+
+
+
+
+ {{ tab.label }}
+
+
+
+
+
+ 筛选查询
+
+ {{ chainLabel }}
+
+ {{ filterForm.startDate || '开始日期' }}
+ {{ filterForm.endDate || '结束日期' }}
+
+
+
+ 查询
+ 重置
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 记录列表
+ 加载中...
+ 暂无记录
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ loadingMore ? '加载更多中...' : (hasMore ? '上滑加载更多' : '没有更多了') }}
+
+
+
+
+
+
+
+
+ onDateChange('start', e)"
+ @confirm="e => onDateConfirm('start', e)"
+ @cancel="closeDatePicker('start')"
+ @close="closeDatePicker('start')"
+ />
+ onDateChange('end', e)"
+ @confirm="e => onDateConfirm('end', e)"
+ @cancel="closeDatePicker('end')"
+ @close="closeDatePicker('end')"
+ />
+
+
+
+
+
+
diff --git a/pages/auth/login.vue b/pages/auth/login.vue
new file mode 100644
index 0000000..add457a
--- /dev/null
+++ b/pages/auth/login.vue
@@ -0,0 +1,255 @@
+
+
+
+
+
+
+
+ 微信一键登录
+
+ 需勾选用户协议与隐私协议,并授权手机号。身份认证需身份证正反面,职业资格证可额外上传。
+
+ 微信授权
+ 手机号授权
+ 身份认证
+
+
+
+
+
+ 授权登录
+
+
+
+
+
+
+ 微信规定:收集个人信息需用户勾选同意并授权手机号。
+
+
+
+ 实名认证
+
+
+ 身份证人像面
+ {{ uploads.front ? '已上传' : '点击上传' }}
+
+ 必填
+
+
+
+ 身份证国徽面
+ {{ uploads.back ? '已上传' : '点击上传' }}
+
+ 必填
+
+
+
+ 职业资格证书
+ {{ uploads.certificate ? '已上传' : '可选上传' }}
+
+ 加分项
+
+ 提交认证
+
+
+
+
+
+
+
diff --git a/pages/bonus/index.vue b/pages/bonus/index.vue
new file mode 100644
index 0000000..acd4b93
--- /dev/null
+++ b/pages/bonus/index.vue
@@ -0,0 +1,328 @@
+
+
+
+
+
+
+
+ 记录
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 历史收入记录
+ 加载中...
+ 暂无记录
+
+
+ {{ item.title }}
+
+ 日期:{{ item.date }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/bonus/withdraw-record.vue b/pages/bonus/withdraw-record.vue
new file mode 100644
index 0000000..9603c43
--- /dev/null
+++ b/pages/bonus/withdraw-record.vue
@@ -0,0 +1,254 @@
+
+
+
+
+
+
+
+ 提现记录
+
+ 查看历史提现申请与审核状态
+
+
+
+
+ 历史提现
+ 加载中...
+ 暂无记录
+
+
+ ¥ {{ item.money }}
+ 申请时间:{{ item.date }}
+
+ {{ payTypeLabel(item.payType) }}
+
+
+ 拒绝原因:{{ item.rejectReason }}
+
+
+ {{ statusLabel(item.applyStatus) }}
+
+
+
+
+
+
+
+
diff --git a/pages/bonus/withdraw.vue b/pages/bonus/withdraw.vue
new file mode 100644
index 0000000..b5f5cde
--- /dev/null
+++ b/pages/bonus/withdraw.vue
@@ -0,0 +1,369 @@
+
+
+
+
+
+
+
+ 提现
+
+ 提交提现申请,到账时间以平台审核为准
+
+
+
+
+ 提现信息
+
+
+
+
+
+ 提现金额
+
+
+
+ 提现方式
+
+ 微信
+
+
+
+
+
+ 支付宝姓名
+
+
+
+ 支付宝账号
+
+
+
+ 开户行名称
+
+
+
+ 银行开户名
+
+
+
+ 银行卡号
+
+
+
+ 备注
+
+
+ 提交申请
+
+
+
+ 提现记录
+
+
+
+
+
+
+
diff --git a/pages/cart/index.vue b/pages/cart/index.vue
new file mode 100644
index 0000000..90d14ec
--- /dev/null
+++ b/pages/cart/index.vue
@@ -0,0 +1,339 @@
+
+
+
+
+
+
+
+ 购物车
+
+ 按商家分单结算,蓝色主色适配竖版,支持多件商品数量调整与勾选。
+
+ 分商家结算
+ 蓝白风格
+ 竖版适配
+
+
+
+
+
+
+
+ 运费:¥{{ vendor.freight }}
+
+
+
+
+
+ {{ item.name }}
+ 规格 {{ item.specs }}
+
+ ¥{{ item.price }}
+ ¥{{ item.origin }}
+
+
+
+ -
+ {{ item.qty }}
+ +
+
+
+
+
+
+
+ 结算信息
+ 已选 {{ selectedCount }} 件,商品金额 ¥{{ goodsTotal }}
+ 预估运费 ¥{{ freightTotal }},合计 ¥{{ totalPrice }}
+
+ 去结算
+
+
+
+
+
+
+
diff --git a/pages/certification/index.vue b/pages/certification/index.vue
new file mode 100644
index 0000000..57b7746
--- /dev/null
+++ b/pages/certification/index.vue
@@ -0,0 +1,276 @@
+
+
+
+
+
+
+
+ 个人信息认证
+
+ 提交身份证正反面识别,职业资格证书可选上传,审核通过后解锁全部功能。
+ 当前状态:{{ statusLabel }}
+
+
+
+
+ 证件上传
+
+
+ 身份证人像面
+ {{ uploads.idCardFront ? '已上传' : '待上传' }}
+
+ 必填
+
+
+
+ 身份证国徽面
+ {{ uploads.idCardBack ? '已上传' : '待上传' }}
+
+ 必填
+
+
+
+ 职业资格证书
+ {{ uploads.qualification ? '已上传' : '可选上传' }}
+
+ 加分项
+
+
+
+
+ 审核流程
+
+ {{ idx + 1 }}
+
+ {{ step.title }}
+ {{ step.desc }}
+
+
+
+
+ 提交审核
+
+
+
+
+
+
diff --git a/pages/exchange/index.vue b/pages/exchange/index.vue
new file mode 100644
index 0000000..ceaafef
--- /dev/null
+++ b/pages/exchange/index.vue
@@ -0,0 +1,623 @@
+
+
+
+
+
+
+
+
+
+ {{ cat.name }}
+ {{ cat.type }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.name }}
+
+ 分类:{{ item.categoryName }}
+
+ ¥{{ item.price }}
+ ¥{{ item.origin }}
+
+
+
+
+ 领券
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 购物车
+ {{ cartCount }}
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/index/components/Progress-Bar/Progress-Bar.css b/pages/index/components/Progress-Bar/Progress-Bar.css
new file mode 100755
index 0000000..ec755ee
--- /dev/null
+++ b/pages/index/components/Progress-Bar/Progress-Bar.css
@@ -0,0 +1,369 @@
+.progress {
+ position: relative;
+ z-index: 5;
+ background: #f3f2f2;
+ /*border: 10px solid #282828;*/
+ border-radius: 20px;
+ display: inline-block;
+ /*box-shadow: 0 5px 17px rgba(40, 40, 40, 0.5), 0 0 3px 1px rgba(40, 40, 40, 0.5) inset;*/
+}
+
+.progress-text {
+ display: none;
+ position: absolute;
+ font-size: 22rpx;
+ text-align: left;
+ padding-left: 10rpx;
+ top: 0;
+ left: 0;
+ color: white;
+}
+
+.progress,
+.progress-bar,
+.progress-text {
+ width: 543rpx;
+ height: 18rpx;
+}
+
+.progress .progress-text:first-child {
+ z-index: 5;
+ color: white;
+}
+
+.progress .progress-text:last-child {
+ z-index: 10;
+ color: black;
+}
+
+.progress-bar {
+ overflow: hidden;
+ position: relative;
+ z-index: 10;
+ border-radius: 12px;
+ width: 0;
+ -moz-transition: width .2s linear;
+ -webkit-transition: width .2s linear;
+ -o-transition: width .2s linear;
+ transition: width .2s linear;
+}
+
+/* Test Style */
+.progress.test .progress-bar {
+ background-color: #00baba;
+ background-image: -moz-linear-gradient(left, #d1fffb 0%, #00baba 100%);
+ background-image: -webkit-linear-gradient(left, #d1fffb 0%, #00baba 100%);
+ background-image: -o-linear-gradient(left, #d1fffb 0%, #00baba 100%);
+ background-image: -ms-linear-gradient(left, #d1fffb 0%, #00baba 100%);
+ background-image: linear-gradient(to right, #d1fffb 0%, #00baba 100%);
+ background-size: 200px 20px;
+ overflow: visible;
+}
+
+.progress.test .progress-text:first-child {
+ display: none;
+}
+
+.progress.test .progress-text:last-child {
+ left: auto;
+ top: auto;
+ bottom: -26px;
+ right: -12px;
+ display: block;
+ background-color: #00baba;
+ width: 40px;
+ height: 20px;
+ border-radius: 3px;
+}
+
+.test[data-width='0%'] .progress-text:last-child {
+ display: none;
+}
+
+.progress.test .progress-text:last-child:before {
+ position: absolute;
+ left: 12px;
+ top: -6px;
+ content: '';
+ width: 0;
+ height: 0;
+ border-style: solid;
+ border-width: 0 6px 6px 6px;
+ border-color: transparent transparent #00baba transparent;
+}
+
+/* Copper style */
+.progress.copper .progress-bar {
+ background-color: #b68d4c;
+ background-image: -moz-linear-gradient(top, #f3e2c7 0%, #b68d4c 51%, #e9d4b3 100%);
+ background-image: -webkit-linear-gradient(top, #f3e2c7 0%, #b68d4c 51%, #e9d4b3 100%);
+ background-image: -ms-linear-gradient(top, #f3e2c7 0%, #b68d4c 51%, #e9d4b3 100%);
+ background-image: linear-gradient(to bottom, #f3e2c7 0%, #b68d4c 51%, #e9d4b3 100%);
+ box-shadow: inset 2px 2px 3px 0 rgba(255, 255, 255, 0.75), inset -3px 0 3px 0 rgba(255, 255, 255, 0.75);
+}
+
+/* Aqua Style */
+.progress.aqua .progress-bar {
+ background-color: #00baba;
+ background-image: -moz-linear-gradient(left, #d1fffb 0%, #00baba 100%);
+ background-image: -webkit-linear-gradient(left, #d1fffb 0%, #00baba 100%);
+ background-image: -o-linear-gradient(left, #d1fffb 0%, #00baba 100%);
+ background-image: -ms-linear-gradient(left, #d1fffb 0%, #00baba 100%);
+ background-image: linear-gradient(to right, #d1fffb 0%, #00baba 100%);
+}
+
+/* Candystripe style */
+.progress.candy .progress-bar {
+ background-color: #f0ad4e;
+ box-shadow: 0 0 6px 2px rgba(255, 255, 255, 0.3) inset;
+ background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.35) 25%, rgba(255, 255, 255, 0) 25%, rgba(255, 255, 255, 0) 50%, rgba(255, 255, 255, 0.35) 50%, rgba(255, 255, 255, 0.35) 75%, rgba(255, 255, 255, 0) 75%, rgba(255, 255, 255, 0));
+ background-repeat: repeat-x;
+ background-size: 20px 20px;
+ -webkit-animation: candystripe 1s linear infinite;
+ -moz-animation: candystripe 1s linear infinite;
+ animation: candystripe 1s linear infinite;
+}
+
+@-webkit-keyframes candystripe {
+ to {
+ background-position: 20px 0;
+ }
+}
+
+@-moz-keyframes candystripe {
+ to {
+ background-position: 20px 0;
+ }
+}
+
+@keyframes candystripe {
+ to {
+ background-position: 20px 0;
+ }
+}
+
+/* Pulsating Neon style */
+.progress.neon .progress-bar {
+ background-color: #fff;
+ box-shadow: 0 0 5px #01fe32 inset, 0 0 16px #08ff35, 0 0 20px #08ff35, 0 0 15px #08ff35;
+ -webkit-animation: pulse 2s linear infinite;
+ -moz-animation: pulse 2s linear infinite;
+ animation: pulse 2s linear infinite;
+}
+
+@-webkit-keyframes pulse {
+ 50% {
+ box-shadow: 0 0 5px #01fe32 inset, 0 0 10px #08ff35, 0 0 10px #08ff35, 0 0 10px #08ff35;
+ }
+}
+
+@-moz-keyframes pulse {
+ 50% {
+ box-shadow: 0 0 5px #01fe32 inset, 0 0 10px #08ff35, 0 0 10px #08ff35, 0 0 10px #08ff35;
+ }
+}
+
+@keyframes pulse {
+ 50% {
+ box-shadow: 0 0 5px #01fe32 inset, 0 0 10px #08ff35, 0 0 10px #08ff35, 0 0 10px #08ff35;
+ }
+}
+
+/* Shine style */
+.progress.shine .progress-bar {
+ background-color: #00BA0C;
+ background-image: -moz-linear-gradient(left, #00BA0C 0%, #00BA0C 75%, #D7FFD1 80%, #00BA0C 85%, #00BA0C 100%);
+ background-image: -webkit-linear-gradient(left, #00BA0C 0%, #00BA0C 75%, #D7FFD1 80%, #00BA0C 85%, #00BA0C 100%);
+ background-image: -o-linear-gradient(left, #00BA0C 0%, #00BA0C 75%, #D7FFD1 80%, #00BA0C 85%, #00BA0C 100%);
+ background-image: -ms-linear-gradient(left, #00BA0C 0%, #00BA0C 75%, #D7FFD1 80%, #00BA0C 85%, #00BA0C 100%);
+ background-image: linear-gradient(to right, #00BA0C 0%, #00BA0C 75%, #D7FFD1 80%, #00BA0C 85%, #00BA0C 100%);
+ background-size: 800px 20px;
+ box-shadow: inset 0 -2px 2px rgba(0, 0, 0, 0.5), inset 0 2px 2px rgba(255, 255, 255, 0.8);
+ -webkit-animation: shine 2s linear infinite;
+ -moz-animation: shine 2s linear infinite;
+ animation: shine 2s linear infinite;
+}
+
+@-webkit-keyframes shine {
+ to {
+ background-position: 800px 0;
+ }
+}
+
+@-moz-keyframes shine {
+ to {
+ background-position: 800px 0;
+ }
+}
+
+@keyframes shine {
+ to {
+ background-position: 800px 0;
+ }
+}
+
+/* Zigzag style */
+.progress.zigzag .progress-bar {
+ background-color: #EFE743;
+ background-image: -moz-linear-gradient(135deg, #EA0000 25%, rgba(234, 0, 0, 0) 25%), -moz-linear-gradient(225deg, #EA0000 25%, rgba(234, 0, 0, 0) 25%), -moz-linear-gradient(315deg, #EA0000 25%, rgba(234, 0, 0, 0) 25%), -moz-linear-gradient(45deg, #EA0000 25%, rgba(234, 0, 0, 0) 25%);
+ background-image: -webkit-linear-gradient(135deg, #EA0000 25%, rgba(234, 0, 0, 0) 25%), -webkit-linear-gradient(225deg, #EA0000 25%, rgba(234, 0, 0, 0) 25%), -webkit-linear-gradient(315deg, #EA0000 25%, rgba(234, 0, 0, 0) 25%), -webkit-linear-gradient(45deg, #EA0000 25%, rgba(234, 0, 0, 0) 25%);
+ background-image: -o-linear-gradient(135deg, #EA0000 25%, rgba(234, 0, 0, 0) 25%), -o-linear-gradient(225deg, #EA0000 25%, rgba(234, 0, 0, 0) 25%), -o-linear-gradient(315deg, #EA0000 25%, rgba(234, 0, 0, 0) 25%), -o-linear-gradient(45deg, #EA0000 25%, rgba(234, 0, 0, 0) 25%);
+ background-image: -ms-linear-gradient(135deg, #EA0000 25%, rgba(234, 0, 0, 0) 25%), -ms-linear-gradient(225deg, #EA0000 25%, rgba(234, 0, 0, 0) 25%), -ms-linear-gradient(315deg, #EA0000 25%, rgba(234, 0, 0, 0) 25%), -ms-linear-gradient(45deg, #EA0000 25%, rgba(234, 0, 0, 0) 25%);
+ background-image: linear-gradient(135deg, #EA0000 25%, rgba(234, 0, 0, 0) 25%), linear-gradient(225deg, #EA0000 25%, rgba(234, 0, 0, 0) 25%), linear-gradient(315deg, #EA0000 25%, rgba(234, 0, 0, 0) 25%), linear-gradient(45deg, #EA0000 25%, rgba(234, 0, 0, 0) 25%);
+ background-position: -10px 0, -10px 0, 0 0, 0 0;
+ background-size: 20px 20px;
+ -webkit-animation: zigzagmove .5s linear infinite;
+ -moz-animation: zigzagmove .5s linear infinite;
+ animation: zigzagmove .5s linear infinite;
+}
+
+@-webkit-keyframes zigzagmove {
+ to {
+ background-position: 10px 0, 10px 0, 20px 0, 20px 0;
+ }
+}
+
+@-moz-keyframes zigzagmove {
+ to {
+ background-position: 10px 0, 10px 0, 20px 0, 20px 0;
+ }
+}
+
+@keyframes zigzagmove {
+ to {
+ background-position: 10px 0, 10px 0, 20px 0, 20px 0;
+ }
+}
+
+/* Diamond style */
+.progress.diamond .progress-bar {
+ background-color: #eee;
+ background-image: -moz-linear-gradient(45deg, red 25%, rgba(255, 0, 0, 0) 25%, rgba(255, 0, 0, 0) 75%, red 75%, red), -moz-linear-gradient(-45deg, red 25%, rgba(255, 0, 0, 0) 25%, rgba(255, 0, 0, 0) 75%, red 75%, red);
+ background-image: -webkit-linear-gradient(45deg, red 25%, rgba(255, 0, 0, 0) 25%, rgba(255, 0, 0, 0) 75%, red 75%, red), -webkit-linear-gradient(-45deg, red 25%, rgba(255, 0, 0, 0) 25%, rgba(255, 0, 0, 0) 75%, red 75%, red);
+ background-image: -o-linear-gradient(45deg, red 25%, rgba(255, 0, 0, 0) 25%, rgba(255, 0, 0, 0) 75%, red 75%, red), -o-linear-gradient(-45deg, red 25%, rgba(255, 0, 0, 0) 25%, rgba(255, 0, 0, 0) 75%, red 75%, red);
+ background-image: -ms-linear-gradient(45deg, red 25%, rgba(255, 255, 255, 0) 25%, rgba(255, 0, 0, 0) 75%, red 75%, red), -ms-linear-gradient(-45deg, red 25%, rgba(255, 0, 0, 0) 25%, rgba(255, 0, 0, 0) 75%, red 75%, red);
+ background-image: linear-gradient(45deg, red 25%, rgba(255, 0, 0, 0) 25%, rgba(255, 0, 0, 0) 75%, red 75%, red), linear-gradient(-45deg, red 25%, rgba(255, 0, 0, 0) 25%, rgba(255, 0, 0, 0) 75%, red 75%, red);
+ background-size: 10px 10px;
+ -webkit-animation: move 1.2s linear alternate infinite;
+ -moz-animation: move 1.2s linear alternate infinite;
+ animation: move 1.2s linear alternate infinite;
+}
+
+@-webkit-keyframes move {
+ to {
+ background-position: 5px -10px;
+ background-size: 20px 20px;
+ }
+}
+
+@-moz-keyframes move {
+ to {
+ background-position: 5px -10px;
+ background-size: 20px 20px;
+ }
+}
+
+@keyframes move {
+ to {
+ background-position: 5px -10px;
+ background-size: 20px 20px;
+ }
+}
+
+.progress.sparkle .progress-bar {
+ background-color: #F7A806;
+ background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAG4AAABACAMAAADMI7KPAAACZFBMVEX////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////xG13JAAAAzHRSTlMAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4/QEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp7fH1+f4CBgoOEhYaHiImKi4yNjo+QkZKTlJWWl5iZmpucnZ6foKGio6SlpqeoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr/AwcLDxMXGx8jJytLua8SRAAASK0lEQVR42nXWWZMb12H28edsvQJoAINl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");
+ box-shadow: inset 0 -2px 2px rgba(0, 0, 0, 0.5), inset 0 2px 2px rgba(255, 255, 255, 0.8);
+ -webkit-animation: sparkle 1500ms linear infinite;
+ -moz-animation: sparkle 1500ms linear infinite;
+ -o-animation: sparkle 1500ms linear infinite;
+ animation: sparkle 1500ms linear infinite;
+}
+
+@-webkit-keyframes sparkle {
+ from {
+ background-position: 0 0;
+ }
+
+ to {
+ background-position: 0 -64px;
+ }
+}
+
+@-moz-keyframes sparkle {
+ from {
+ background-position: 0 0;
+ }
+
+ to {
+ background-position: 0 -64px;
+ }
+}
+
+@-o-keyframes sparkle {
+ from {
+ background-position: 0 0;
+ }
+
+ to {
+ background-position: 0 -64px;
+ }
+}
+
+@keyframes sparkle {
+ from {
+ background-position: 0 0;
+ }
+
+ to {
+ background-position: 0 -64px;
+ }
+}
+
+.progress.hearts .progress-bar {
+ background-image:
+ radial-gradient(circle closest-side at 60% 43%, #bb0033 26%, rgba(187, 0, 51, 0) 27%),
+ radial-gradient(circle closest-side at 40% 43%, #bb0033 26%, rgba(187, 0, 51, 0) 27%),
+ radial-gradient(circle closest-side at 40% 22%, #dd3355 45%, rgba(221, 51, 85, 0) 46%),
+ radial-gradient(circle closest-side at 60% 22%, #dd3355 45%, rgba(221, 51, 85, 0) 46%),
+ radial-gradient(circle closest-side at 50% 35%, #dd3355 30%, rgba(221, 51, 85, 0) 31%),
+ radial-gradient(circle closest-side at 60% 43%, #bb0033 26%, rgba(187, 0, 51, 0) 27%),
+ radial-gradient(circle closest-side at 40% 43%, #bb0033 26%, rgba(187, 0, 51, 0) 27%),
+ radial-gradient(circle closest-side at 40% 22%, #dd3355 45%, rgba(221, 51, 85, 0) 46%),
+ radial-gradient(circle closest-side at 60% 22%, #dd3355 45%, rgba(221, 51, 85, 0) 46%),
+ radial-gradient(circle closest-side at 50% 35%, #dd3355 30%, rgba(221, 51, 85, 0) 31%);
+ background-color: #b03;
+ background-position: 0 0, 0 0, 0 0, 0 0, 0 0, 20px 20px, 20px 20px, 20px 20px, 20px 20px, 0 0;
+ background-size: 40px 40px;
+ -webkit-animation: heartmove 1.2s linear infinite;
+ -moz-animation: heartmove 1.2s linear infinite;
+ animation: heartmove 1.2s linear infinite;
+}
+
+@-webkit-keyframes heartmove {
+ to {
+ background-position: 20px 0;
+ }
+}
+
+@-moz-keyframes heartmove {
+ to {
+ background-position: 20px 0;
+ }
+}
+
+@keyframes heartmove {
+ to {
+ background-position: 40px 0, 40px 0, 40px 0, 40px 0, 40px 20px, 40px 20px, 40px 20px, 40px 20px, 40px 20px, 40px 0px;
+ }
+}
+
+h2 {
+ text-align: center;
+ font-size: 18px;
+ font-family: Arial;
+}
+
+a {
+ color: #89310F;
+}
\ No newline at end of file
diff --git a/pages/index/components/Progress-Bar/Progress-Bar.vue b/pages/index/components/Progress-Bar/Progress-Bar.vue
new file mode 100644
index 0000000..8cb088c
--- /dev/null
+++ b/pages/index/components/Progress-Bar/Progress-Bar.vue
@@ -0,0 +1,57 @@
+
+
+
+ {{ Width + '%' }}
+ {{ Width + '%' }}
+
+
+
+
+
+
+
diff --git a/pages/index/index.vue b/pages/index/index.vue
new file mode 100644
index 0000000..61a2012
--- /dev/null
+++ b/pages/index/index.vue
@@ -0,0 +1,1400 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 自主填报
+
+
+
+ 填报记录
+
+
+
+
+
+
+
+
+
+
+ {{ tab }}
+
+ {{ pendingCount }}
+
+
+
+
+
+
+
+ {{ cat.title }}
+
+
+
+
+
+
+
+
+ {{ currentCity }}
+
+
+
+ {{ sort }}
+ ▼
+
+
+
+
+
+
+
+
+
+
+
+ {{ formatTaskDateText(task) }}
+ {{ formatTaskDateSubText(task) }}
+ {{ task.cate.title }}
+
+
+ ¥
+ {{ calReward(task) }}
+
+
+
+ {{ task.merchant.name }}
+ {{ task.chainText }}
+ {{ task.cate.title }}
+
+ {{ task.title }}
+
+ {{ task.merchant.province }}{{ task.merchant.city }}{{
+ task.merchant.region
+ }}{{ task.merchant.address }}
+
+
+
+ {{ task.distance }}
+
+
+
+
+ 总进度
+ {{ totalProgressText(task) }}
+
+
+
+
+
+
+
+ 订单状态:
+ {{ auditLabel(task.userTask.status) }}
+
+ 原因: {{ task.userTask.auditReason }}
+
+ 查看详情
+
+
+ 查看详情
+ 接单
+
+
+
+ 查看详情
+ 去执行
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/mine/index.vue b/pages/mine/index.vue
new file mode 100644
index 0000000..4b962b1
--- /dev/null
+++ b/pages/mine/index.vue
@@ -0,0 +1,984 @@
+
+
+
+
+
+
+
+
+ {{ isLoggedIn ? user.nickname : '未登录' }}
+ {{ isLoggedIn ? user.phone : '点击登录同步手机号' }}
+
+ 去登录
+
+
+
+ {{ currentCity }}
+
+
+
+ 转发分享给新用户,可获得积分
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ pointSummary.currentPoints }}
+ 积分
+
+ 我的积分
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 自主填报
+
+
+ 客户拜访
+
+
+
+
+ 药审核
+
+
+
+
+
+ 用户资料
+
+
+
+ 全部订单
+
+
+
+ 领券中心
+
+
+
+ 我的积分
+
+
+
+
+ 服务内容
+
+
+
+ 我的分享
+
+
+
+ 收货地址
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 个人信息认证
+
+
+
+ 差旅报销
+
+
+
+
+
+ 慢病管理
+
+
+
+ 慢病信息收集
+
+
+
+ 慢病信息管理
+
+
+
+ 联系客服
+
+
+
+
+
+
+ 退出登录
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/mine/profile.vue b/pages/mine/profile.vue
new file mode 100644
index 0000000..bc6202c
--- /dev/null
+++ b/pages/mine/profile.vue
@@ -0,0 +1,261 @@
+
+
+
+
+
+
+
+ 用户资料
+
+ 点击头像上传,点击昵称获取微信昵称
+
+
+
+
+
+
+ 用户资料
+
+
+
+ 点击头像上传
+
+
+
+
+
+
+ 姓名
+
+
+
+
+
+
+
+
+
+ 昵称
+
+
+
+
+
+
+ 保存
+
+
+
+
+
+
diff --git a/pages/order/checkout.vue b/pages/order/checkout.vue
new file mode 100644
index 0000000..0cfc966
--- /dev/null
+++ b/pages/order/checkout.vue
@@ -0,0 +1,768 @@
+
+
+
+
+
+
+
+ 收货信息
+
+
+
+ {{ selectedAddress.name }} {{ selectedAddress.phone }}
+ 请选择收货地址
+
+
+ {{ getFullAddress(selectedAddress) }}
+ 点击选择或新增收货地址
+
+
+
+ 默认
+ 更换
+
+
+
+
+ 商品信息
+
+
+
+
+ {{ item.goodsName }}
+ x {{ item.num }}
+
+
+ ¥{{ (item.num * item.salePrice).toFixed(2) }}
+
+
+
+
+
+ 优惠
+
+ 优惠券
+ {{ couponText }}
+
+
+
+
+
+
+
+
+ 费用明细
+
+ 商品金额
+ ¥{{ goodsTotal }}
+
+
+
+
+
+
+ 优惠
+ -¥{{ discount }}
+
+
+ 应付总额
+ ¥{{ payTotal }}
+
+
+
+
+ 合计:¥{{ payTotal }}
+ 提交订单
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/order/detail.vue b/pages/order/detail.vue
new file mode 100644
index 0000000..8ce08e0
--- /dev/null
+++ b/pages/order/detail.vue
@@ -0,0 +1,256 @@
+
+
+
+
+
+
+
+ 订单信息
+
+ 订单号
+ {{ getOrderNo(order) }}
+
+
+ 状态
+ {{ statusLabel(order.status) }}
+
+
+ 下单时间
+ {{ order.createTime || order.createdAt }}
+
+
+
+
+ 收货地址
+ {{ receiver.name }} {{ maskPhone(receiver.phone) }}
+ {{ receiver.address }}
+
+
+
+ 商品清单
+ 暂无商品
+
+
+ {{ item.goods.goodsName }}
+ x {{ item.num || item.qty || 1 }}
+ ¥{{ formatPrice(item.goods.salePrice || item.price || item.amount || 0) }}
+
+
+
+
+
+ 费用明细
+
+ 商品金额
+ ¥{{ formatPrice(order.totalPrice || 0) }}
+
+
+ 运费
+ ¥{{ formatPrice(order.freightAmount || order.freight || 0) }}
+
+
+ 优惠
+ -¥{{ formatPrice(order.discountAmount || order.discount || 0) }}
+
+
+ 应付总额
+ ¥{{ formatPrice(order.orderPrice || order.payPrice || order.amount || 0) }}
+
+
+
+
+
+
+
+
diff --git a/pages/order/list.vue b/pages/order/list.vue
new file mode 100644
index 0000000..f49d9f3
--- /dev/null
+++ b/pages/order/list.vue
@@ -0,0 +1,240 @@
+
+
+
+
+
+
+
+ 加载中...
+ 暂无订单
+
+
+
+ 订单号:{{ getOrderNo(order) }}
+ {{ statusLabel(order.orderStatus) }}
+
+
+ 下单时间:{{ order.createTime || order.createdAt }}
+
+
+
+ {{ item.goodsName || item.name || item.title }}
+ x {{ item.num || item.qty || 1 }}
+ ¥{{ formatPrice(item.salePrice || item.price || item.amount || 0) }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/performance/index.vue b/pages/performance/index.vue
new file mode 100644
index 0000000..43e0062
--- /dev/null
+++ b/pages/performance/index.vue
@@ -0,0 +1,996 @@
+
+
+
+
+
+
+
+ {{ stats.doneNum }}/{{ stats.taskNum }}
+ 我的完成进度
+
+
+
+
+
+
+ {{ stats.doneRate }}%
+ 我的完成率
+
+
+
+
+ {{ stats.totalDoneNum }}/{{ stats.totalTaskNum }}
+ 总完成进度
+
+
+
+
+
+
+ {{ stats.totalDoneRate }}%
+ 总完成率
+
+
+
+
+
+
+
+
+ {{ currentCity }}
+
+
+
+
+
+
+
+
+
+
+ {{ merchantLabel }}
+ {{ manufacturerLabel }}
+
+
+
+
+
+
+ 匹配门店:
+ {{ item.merchant.name || '-' }}
+
+
+
+ 任务名称:
+ {{ item.title }}
+
+
+ 当前状态:
+ {{ resolveTaskStatusLabel(item) }}
+
+
+ 任务时间:{{ item.date || '-' }}
+
+
+ 距离{{ item.distance || '100米' }}
+
+
+
+
+
+
+
+
+
+ {{ item.doneNum }}/{{ item.num }}
+
+
+
+ 查看更多
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ idx + 1 }}
+ {{ person.nickname || person.name || '-' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+ 药店筛选
+ 确定
+
+
+
+
+
+
+
+
+
+ {{ item.text }}
+
+
+
+
+
+
+
+
+ 取消
+ 厂家筛选
+ 确定
+
+
+
+
+
+
+ {{ item.text }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/product/detail.vue b/pages/product/detail.vue
new file mode 100644
index 0000000..a19fda3
--- /dev/null
+++ b/pages/product/detail.vue
@@ -0,0 +1,289 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ¥{{ product.salePrice }}
+ ¥{{ product.originPrice }}
+ 领券
+
+ {{ product.goodsName }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 图文详情
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 加入购物车
+ 立即购买
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/share/components/ShareCreatePopup.vue b/pages/share/components/ShareCreatePopup.vue
new file mode 100644
index 0000000..6b5bbcf
--- /dev/null
+++ b/pages/share/components/ShareCreatePopup.vue
@@ -0,0 +1,942 @@
+
+
+
+
+
+
+
+
+ 取消
+ 选择门店
+ 确定
+
+
+
+
+
+
+
+
+
+ {{ item.text }}
+
+
+
+
+
+
+
+
+ 取消
+ 选择商品
+ 完成
+
+
+
+
+
+
+
+
+ 暂无商品
+
+ {{ item.goodsName || '-' }}
+ 厂家:{{ item.makerAbb || '-' }}
+ 规格:{{ item.spec || '-' }}
+
+ 编码:{{ item.goodsCode || '-' }}
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/share/create.vue b/pages/share/create.vue
new file mode 100644
index 0000000..5fcaa78
--- /dev/null
+++ b/pages/share/create.vue
@@ -0,0 +1,1766 @@
+
+
+
+
+
+
+
+
+ 连锁门店
+
+
+
+ {{ form.merchantId ? `${form.chainName || '-'}/${form.merchantName || '-'}` : '请选择连锁和门店' }}
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+
+
+
+
+
+
+
+
+ 暂无商品
+ 无匹配商品
+
+
+ {{ detail.goodsName || '-' }}
+
+
+
+
+ 暂无图片
+
+
+
+
+
+
+ 厂家:{{ detail.makerAbb || '-' }}
+ 规格:{{ detail.spec || '-' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 已选择商品列表
+ 暂未选择商品
+
+
+ {{ item.goodsName || '-' }}
+
+ 数量:{{ item._num }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 仅能上传一张小票
+
+
+
+
+ {{ receiptUploading ? '上传中...' : (form.receiptUrl ? '已上传,点击重新上传' : '点击重新上传') }}
+
+
+
+
+ {{ receiptUploading ? '上传中...' : '上传小票' }}
+
+
+
+
+
+ 提交
+
+
+
+
+
+ 取消
+ 选择连锁和门店
+ 确定
+
+
+
+
+
+
+
+
+
+ {{
+ item.text
+ }}
+
+
+
+ {{
+ item.text
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/share/detail.vue b/pages/share/detail.vue
new file mode 100644
index 0000000..71d95ed
--- /dev/null
+++ b/pages/share/detail.vue
@@ -0,0 +1,329 @@
+
+
+
+
+
+
+
+ 基本信息
+ 加载中...
+
+
+ 门店
+ {{ detailEntry.merchantName || detailEntry.merchant_name || '未命名门店' }}
+
+
+
+
+
+
+ 提交人
+ {{ detailEntry.owner || '-' }}
+
+
+ 日期
+ {{ detailEntry.date || detailEntry.activityDateText || '-' }}
+
+
+ 留言
+ {{ detailEntry.remark || '-' }}
+
+
+
+
+
+ 小票查看
+
+
+
+ 暂无小票
+
+
+
+ 商品明细
+
+
+
+ {{ detail.goodsName || detail.goods_name || detail.name || detail.title || '商品' }}
+
+
+
+ 数量
+ x {{ detail.num || detail.qty || 0 }}
+
+
+ 厂家
+ {{ resolveManufacturer(detail) }}
+
+
+ 编号
+ {{ resolveCode(detail) }}
+
+
+ 规格
+ {{ resolveSpec(detail) }}
+
+
+ 金额
+ {{ formatMoney(resolveAmount(detail)) }}
+
+
+
+
+
+
+
+
+ 暂无明细
+
+
+
+
+
+
+
diff --git a/pages/share/index.vue b/pages/share/index.vue
new file mode 100644
index 0000000..dbc230b
--- /dev/null
+++ b/pages/share/index.vue
@@ -0,0 +1,1639 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ data.num }}
+ 个
+
+ 我的晒单数量
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ data.totalNum }}
+ 个
+
+ 合计晒单数量
+
+
+
+
+
+
+
+
+ {{ currentCity }}
+
+
+
+
+ 更多
+
+
+ {{ merchantLabel }}
+
+
+
+
+
+
+ 匹配门店:
+ {{ item.merchantName }}
+
+
+ 订单号:
+ {{ item.orderNo }}
+
+
+ 分享时间:{{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }}
+
+
+ 距离{{ item.distance || '100米' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 更多>>
+
+
+
+
+
+
+
+ 搜索
+
+
+
+
+
+
+
+
+ {{ item.goodsName }}
+
+ {{ item.product ? item.product.spec : item.spec }}
+
+ {{ item.product ? item.product.makerAbb : '' }}
+
+
+
+
+
+
+
+
+
+
+ 共分享{{ item.totalNum ? item.totalNum : 0 }}次
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ idx + 1 }}
+
+ {{ item.merchantName || item.merchant_name || '门店' }}
+
+
+
+ 晒单 {{ item.shareList.length || 0 }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+ 选择连锁和门店
+ 确定
+
+
+
+
+
+
+
+
+
+ {{
+ item.text
+ }}
+
+
+
+ {{
+ item.text
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/share/mine.vue b/pages/share/mine.vue
new file mode 100644
index 0000000..326f9f8
--- /dev/null
+++ b/pages/share/mine.vue
@@ -0,0 +1,727 @@
+
+
+
+
+
+
+
+ 我的分享
+
+ 查看已提交的晒单记录
+
+
+
+
+ 筛选查询
+
+ {{ chainLabel }}
+ {{ filterForm.startDate || '开始日期' }}
+ {{ filterForm.endDate || '结束日期' }}
+
+
+
+ 查询
+ 重置
+
+
+
+
+ 记录列表
+ 加载中...
+ 暂无分享记录
+ 暂无匹配记录
+
+
+ 门店:{{ item.merchantName || item.merchant_name || '未命名门店' }}
+
+
+
+
+ 提交人:{{ item.owner || '-' }} · {{ item.date || item.activityDateText }}
+ 留言:{{ item.remark || '-' }}
+
+ 点击查看详情
+
+
+
+
+ {{ deletingId === item._rowKey ? '作废中...' : '作废' }}
+
+
+
+
+
+
+
+ onDateConfirm('start', e)"
+ @cancel="closeDatePicker('start')"
+ @close="closeDatePicker('start')"
+ />
+ onDateConfirm('end', e)"
+ @cancel="closeDatePicker('end')"
+ @close="closeDatePicker('end')"
+ />
+
+
+
+
+
+
diff --git a/pages/task/detail.vue b/pages/task/detail.vue
new file mode 100644
index 0000000..768e55a
--- /dev/null
+++ b/pages/task/detail.vue
@@ -0,0 +1,3570 @@
+
+
+
+
+
+
+
+
+ {{ task.title }}
+ {{ taskTag }}
+
+
+ {{ storeName }}
+
+ 所属连锁
+ {{ storeChainText }}
+
+
+
+ {{ storeAddress }}
+
+
+
+
+
+ 总进度
+ {{ totalProgressText }}
+
+
+
+
+
+
+
+
+ 任务详情
+
+ {{ detailExpanded ? '收起' : '展开' }}
+
+
+
+
+
+
+
+
+
+
+
+ 任务要求
+
+ {{ detailRequirement }}
+
+
+
+
+
+
+
+
+
+
+
+ 活动政策
+
+ {{ detailPolicy }}
+
+
+
+
+ 任务执行时间段
+
+ {{ taskExecutePeriod }}
+
+
+
+
+
+
+
+
+
+
+
+
+ 上班打卡(水印拍照)
+
+
+
+
+
+
+
+
+
+
+
+ {{ (clockPhotos[0] && clockPhotos[0].time) || '--:--' }}
+ {{ (clockPhotos[0] && clockPhotos[0].date) || '--' }}
+
+
+
+
+ 纬度:
+ {{ clockPhotos[0].lat }}
+
+
+ 经度:
+ {{ clockPhotos[0].lng }}
+
+
+ 详细地址:
+ {{ clockPhotos[0].location }}
+
+
+
+
+
+
+
+
+ 巡店列表
+
+
+ 暂无巡店记录
+
+
+
+ 巡店{{ idx + 1 }}
+ {{ item.visitTime }}
+
+
+ 删除
+
+
+
+ 选择门店
+
+ {{
+ item.merchantName ? `${item.chainName || '-'}/${item.merchantName}` : (merchantLoading ? '门店加载中...' : '请选择连锁和门店')
+ }}
+
+
+
+
+ 定位
+ 获取定位
+
+
+
+ 纬度:
+ {{ formatCoordinate(item.lat) }}
+
+
+ 经度:
+ {{ formatCoordinate(item.lng) }}
+
+
+ 详细地址:
+ {{ item.location || '暂无地址' }}
+
+
+
+
+
+ 打卡拍照
+
+
+
+
+
+
+ 暂无巡店照片
+
+
+
+
+ x
+
+
+
+
+
+
+
+
+ 新增巡店
+
+
+
+
+
+ 门头摆台照
+
+
+ 暂无门头摆台照
+
+
+
+ x
+
+
+
+
+
+
+
+
+
+ 销量填写
+
+
+
+
+
+ 新增商品
+
+
+
+ {{ isAuditEntry ? '暂无销量商品' : '请点击“新增商品”逐个添加销量商品' }}
+
+
+
+
+ {{ group.title }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.name }}
+
+
+
+ 删除
+
+
+
+
+
+ {{ salesProgressText(item) }}
+
+
+
+
+ {{ item.spec }}
+
+
+
+
+ {{ item.unit }}
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+ 选择商品
+ 完成
+
+
+
+
+
+
+
+
+
+
+ 暂无可选商品
+ 该分类暂无可选商品
+
+
+
+ {{ item.name }}
+ {{ item.makerAbb || '-' }} / {{ item.spec || '规格' }}
+
+
+ {{ isProductSelected(item) ? '已添加' : '添加' }}
+
+
+
+
+
+
+
+
+
+
+ 小票上传
+ (必须原图)
+
+
+
+
+ 暂无小票
+
+
+
+
+
+ 点击拍照上传
+ 支持jpg/png,需清晰可见
+
+
+
+
+
+
+ x
+
+
+
+
+
+
+
+
+ 活动小结
+
+
+
+
+ 下班打卡(水印拍照)
+
+
+
+
+
+
+
+
+
+
+
+ {{ (clockPhotos[1] && clockPhotos[1].time) || '--:--' }}
+ {{ (clockPhotos[1] && clockPhotos[1].date) || '--' }}
+
+
+
+
+ 纬度:
+ {{ clockPhotos[1].lat }}
+
+
+ 经度:
+ {{ clockPhotos[1].lng }}
+
+
+ 详细地址:
+ {{ clockPhotos[1].location }}
+
+
+
+
+
+
+
+
+ 门店评分
+
+
+
+
+
+
+
+
+ 提交
+
+
+ 审核中
+ 审核通过
+
+
+ 通过
+ 驳回
+
+
+ 抄送
+
+
+
+
+
+ 取消
+ 选择连锁和门店
+ 确定
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.text }}
+
+
+
+
+ {{ item.text }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/visit/index.vue b/pages/visit/index.vue
new file mode 100644
index 0000000..85360dd
--- /dev/null
+++ b/pages/visit/index.vue
@@ -0,0 +1,748 @@
+
+
+
+
+
+
+
+ 培训拜访
+
+ 记录培训拜访现场定位、沟通内容和照片,提交后可回看历史记录。
+
+
+
+
+ 新增拜访
+
+
+ 定位信息
+
+
+ {{ visitForm.location || '定位中,或点击右侧重新定位' }}
+
+
+ {{ formatCoordinate('纬度', visitForm.lat) }}
+ {{ formatCoordinate('经度', visitForm.lng) }}
+
+
+ 重新定位
+
+
+
+ 连锁
+
+
+ {{ visitForm.chain || (chainLoading ? '连锁加载中...' : '请选择连锁') }}
+
+
+
+
+
+ 拜访人员
+
+
+
+
+ 沟通内容
+
+
+
+
+
+ 拍照上传
+ {{ photos.length ? `已上传 ${photos.length} 张,点击继续拍照` : '点击拍照上传,最多 9 张' }}
+
+ 必填
+
+
+
+
+
+ x
+
+
+
+
+ 活动小结
+
+
+
+ 提交拜访
+
+
+
+
+ 历史记录
+ 加载中...
+ 暂无记录
+
+
+
+
+ {{ item.name || '未命名拜访' }}
+ {{ item.createdAt || '暂无时间' }}
+
+
+ {{ item.status }}
+
+
+
+
+ 定位:{{ item.location || '-' }}
+ 纬度:{{ item.lat || '-' }}
+ 经度:{{ item.lng || '-' }}
+ 连锁:{{ item.chain || '-' }}
+ 沟通内容:{{ item.content || '-' }}
+ 活动小结:{{ item.summary || '-' }}
+ 照片:
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/static/images/addCart.png b/static/images/addCart.png
new file mode 100644
index 0000000000000000000000000000000000000000..d2ed30e844c23cf6cd7b0eaa000aca2d6d0bcc95
GIT binary patch
literal 6601
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
literal 0
HcmV?d00001
diff --git a/static/images/blob-scene-haikei.png b/static/images/blob-scene-haikei.png
new file mode 100644
index 0000000000000000000000000000000000000000..1bf2cfca89e628c069f28dbf4e870e10b47e169a
GIT binary patch
literal 2786
zcmb_e3se(V8jghW5@Qe*SJ93micu$-BqSjtQC`wu8_WSJ;A6vNG6^Xp6O#b~q7{6x
z&{d9T!PP}nL|O1z3ckQfiy)#X3bLT}Q69cfizrlE*a@#ykGtE`vvba!xsUJu|Ns5p
zz5kiN$b&u^<1pEQMx%`hl==r#UlrB->};s_7oX-fQy+VSG{Quq*|=NUC|PJz6^-`M
zU2SMM87}(_!3Z52RS`;@ZPpnmG>zu9&}=}lXq*I;xJIj&fX~jH0|Bi{0)`1>u*~3x
zM`@*rMm!`jC=^SK#>6Uc;R3+Rj8F!2IEeygU5ws@m?hwVU4-gera2%m03o9#ppV5N
z5H6DgeuNPR#B45v!7v{XiP>VI7)CusYHS|BgLwiD%;&&DhzBDA7~u+mp$DWK8dYi}
z*gs&%9W|1GQ6yIWO&B#Cu2?MQz&s9*2T=&fl%OY3Go&}o7`EV#n=qr+
zKxzp+V6liQiC9tsQo#yYv#{ngTOg7@PgRhiY(+
zL9YRUkyIgmL=0i198=Np2cnS4kU+hOMD-XR=q~{&3T&-bgJLop>L6ruR=et!aswWv3|Uq$Wr5OUS9P|1V1Vq6V*hM?eE5t5JQ14<%$A{;R&P1=
ztl{XeC{(Y(sS3maIe(bW@00Vt(0Thn{y*t(EOlp4D9-yD4y?UZqbT+RQ&i!zbVrIV
zH5e(|xSq8o-zU_(yK|RrNy}p9#=4?Tx%5^TWx!`!oM}Qe<+Q>t;Rv^7lJS1>1rbdxlh6JF@QSrM(*GpL>2wJsQ7%!P>_&
z);ij6#VW$NzN0kroN_0eKNw)RZQu9EN{_sGz0axG%GyaKUMS_w7{;+0kYJ8MrCTPczoPtKp-_1M&yV^PkUsJ=sUwyf0)<
zQqDKF`|68TvFi)c(Xhlc2mEvI3hPBi8CT<
z=0ML+kLkU&44Mw)Lsw>2U0K0!D~$Q%Z@Wt#FyfYPs3@+SnEi4CGRxUErNgIv2K{I!
z5uN!4aY=|Pt}E}8b}$28NyW8h&Jjd_-7u%8&qv-o`qw6!5*DP(?lH}}5{Jn{Op0}oK
zFWm$j-Ol*xm)fwK-9Cno$DPhN=1|fP7R~y>%cHs0mr=wzdf1)OO4t1yo6CQY9l4sF
zDzIMiY@XrC)33Sho9=DA6A>xS7@L~hCd_syv{IMm-MD^%c`XLbEDxU|IrkzhNSXFi
zA)}SHOfX+#P#?z>^ZVNN87Fv3XL()N0Bx?#UI_}NC(2vfYm;4l9ock+ey&oqbTTbY
zkyUnAUo8(PWLa0I7rI#e=|!72!_6wP=fI=5r+2^ZJ^d5yVp({7dUDvo)*FkenB#8p
zjaN#yO&&9TPAMrS=kPk+df$jo<`qK%K><bgWw?
zS8fWXAsI6cSr?CwvAMJKWg}X7G564=OHXG1*wpF0C9ghqheyD}-0ByZUDFyJSy#h9
zb*;YUF_m_36q}h}cWHIE##JiW;qB5r{&K=7OFCm?Fm?%v2avso&9L5uI93`!g0*a
ttCqbF*myB{-e(LIN}0=~pFh#<%t~r)*gSuBl;tlpa8Z!|5#Pv^e*x{YD((OP
literal 0
HcmV?d00001
diff --git a/static/images/coupon.png b/static/images/coupon.png
new file mode 100644
index 0000000000000000000000000000000000000000..131bb9720c933bc3832fcafec1bcbfd1a892c95a
GIT binary patch
literal 5970
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
literal 0
HcmV?d00001
diff --git a/static/logo.png b/static/logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..b5771e209bb677e2ebd5ff766ad5ee11790f305a
GIT binary patch
literal 4023
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
A^#A|>
literal 0
HcmV?d00001
diff --git a/static/tabbar/group.png b/static/tabbar/group.png
new file mode 100644
index 0000000000000000000000000000000000000000..556c1ed5e8a0cc73fbb324d817c7f5e800268798
GIT binary patch
literal 21255
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