commit 69e6ed8c597fe348d5206f89c6849483d0e4b699
Author: b2894lxlx <517289602@qq.com>
Date: Tue Sep 29 15:17:13 2026 +0800
20260929
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/AwcLDxMXGx8jJytLua8SRAAASK0lEQVR42nXWWZMb12H28edsvQJoAINlMMPZOCQlioso0rKlmPYb+XXKvnAlvslNrvIBkg+Rz5CkKpU9lcSViiuJs5Ys2XKsxZJC2+K+zMLZBxgM1qXR6zknNYOhSMrM7wrVhcIfDwp9qsnl/58nB993NgGQbJn0Wqw61arjGVPM/PpFQ473/gwwpi0yalS/u+BwdfffiQU1bALEnTPWfLwIn/Nu4tpbiwWi7/zdLvhwlJO9JgCesc/EkckO9lspntUG3vcq+g8AIO1HtA7kMkzDLgYa1ACw7MeJwAtRxWf3BmaOksiyecq33r/Uv8e0MkrFaqU9tirXmj/HF2SvXWge16D6fQqESlKtaL5nQsbgV7/RvrsjicYLxFtbYKljQI+62RRcrW/kzFlm2F2DjUz79QSj1iaep5PRKp5QAPE7jMg+axaJ72e++ltn9Dff/mCI/8vyqz98c8nySQRw03PlGwamz6/8uB7gOgSsxf34ySwmDgGM/vM/AWb5OJGuj2wtG3fDfQC5105naPZ6fw3PcX2csBZqw58cTg/v3AP4NeUtGpTwqPcgYjWPKtgX/hsTpTfm7ebfYqKwuJz8M475/zO/5PY+7eLIMGQMiGcuhrw5SHGsfK7WutXFRLxis6nba72D3AC8zIqvqnUar9yUaYooNFnnMU5YV4tk+tvvHW/N/7qZi3+3+69nZ37up53OzdNBR4JLDRExRInMLdYJ0CEawNkrFVHK/Acm1GiQV+3Pyr9N/hCcEroNpomsrcVq88evLYYP3sWJMy6nxgI/yvGLb2RucpG5Ouz6OPIY1FtIW00YSbLf3hEO4fBMGYUAMmVLWstXf4mJcYON+jo358zucWqAh5JLOhooyh+vXgvqmKCGSxiSQgIAJO3HGtror2OCKeP0rKg0Md55rPaVr/YZBxEkTYF+bID0WjgRPHwIoHXfypxmZxAFLYuG/fuxWHz9OwvvHoY4YroIB54r1j5sSAB6aMUU8B/6AMxz5+aSYemMqeVwLDc/9KvBH3UsxtJBSHUCkHOulJurMY6IYoHGAMJ6A4RNy+4Hfc2HG6R6/a0rV7+09FMc4dOznmqtpLs3H0fq+II5r1LWuAVwYi16dtKE4aXxXggdjiU9HLGURi1NdAQEcSfprDY1AEyLWk72ARBi0ZTf6TJ+GGcratWay3nZ9Mq33wYAdz7n93R0Q+k0BYBCSYyd6EYbMIt1EBAL4Ya8g2P7pN6gfeIwaBkAwFqMCWf5/wWt/jbcWKdB0wXvQiVwMo4x6iSExjYf4sj0XK7d35fjgq2bIcDLX3E0EeN9QMfZ8dqs3KBp/w5O7O3V6uAR5zpKASDGifzXTiPs9V8qrfu5Tt0xOKB1YnmenJNmOmTO5p5dCIOZCxlSWL7FjLIj031A1BwBQuYPRojbyDUaeA4v/Nrg3VQySkM8K/fGLLSVrRbEwrjcSQk4AKhsTmKmFPkFu/vOudOzzr2DLNOUXc7ZlUeEA2AuBWAV3EACM+fz/yQSPCPlKgFVKb7AqhoAIT4jvSx026PHuZkL4ArkYfM3zYekUDb5xV+OTKL9i8tm0F/tAnALbPK1DgD75W9e/O4ffwImnk6p/wsoflXzs4prJJJ8OKBTpw6SfvY4N18wFAjI6l+/PkSYODItbmjNoxxPjXHTB0BbGc6SeG8VgFHL5aa+9snU0mAFTxFKqNQAIUQyiROf5L467Ky/D4p2WyPpcQCi17eJ1ox3nENTj29co61+a0fsL70i2TjXBYC6cq+nw1/+CACYHI0s/Z1B9RZA9NMeAQE0PAsDH098NOoPdgBNoAEoBkBFyhNi3Nvc0u1h+HFv7ycPk1xGjlSvkq7/ACCCy6kF19oNHwJA0Eta7yu3jJ8AnCkw20iPwsUxBXEqlgERS5xI6gcDAEQRrQEQHMle/vLo8eo2AEpTADBfPyW/DzEdT+MWcbKEv7pkatb7UxzLXT1tQQePPoZISL5gqj3lzeRlglWeNXwFOe7keBsvwHFk+FF3cJgAgFKTDywYCkg6v74chLseJdki01A4ZkhRdQ0DcR5IQKeKREm36phn3OH0SoYmZpcUZ8o59k8APHsch8/lJu6DaDw1rotgdg/cszxvl0GHHTMHqacbACu/OXVKEarzPRBuz6YExMnnrITns21CrOnl+M58jpvjb/0QzuLpqN3cwOcYnrLmejiR7D9YG4KY4lR/fUQZEOV45A/CA8BYOi3i2IEKt7dyp18uezqVrVwxD5OjwVwRFOOVYpFqbln7S6/k3HzV3X9+3QT/yqX8zt/gc4TTMFoNioeBHfto+EmkpRVCjcfot14R3e0dzhcNWMNOfFgKB4ZocvGKMPZ/2lMq5SDKLJ+3oDm3RPKrOX7pK2V17nfeaX1eswihgWZmc3rQIaGCjg8SINlISiS51bFqUYzI0pFS2xgrRcWgMsWXnVbyV5mQa6KCrZoQGpDsaY7hCVW4ZCi0P3k6jgOBhU4raAdMCilp+/jvJLtDg40zX79QSvZnDB3drjZAMyrqBxkjc4rRTI9ejsbcv78mFkCJmXVmzwwC/YV1aLUyVG48exBS2QmsYQDmidYGN4wnw6P9/NSFl7lNe3fdjN+/CXR3qlyLojOOAb/ev7enPX4fu3cvUW6Uy2Pt+8Mv5g5vDKPww8kyDWgV0gRxH4B9SkQsHuCJKBpZS9UCkvntzcntq7oFppP1kBlT4/WDcRu0AQCbXi2uxAr04v6jL+bInQfUxxGDhgC0lJgYPT5zzdYPHuNzUwtli8nUKdbDk6frgwL8UZfttzcHPigmogf18cWsDZ2tfiHnelm1iiNm1suGD/EsXs0LOdsY45gZua98q5iG4I4IMaEbMQvHFd7r1TWTTtVmpNGk1Vr9MKVcyb01PJerXC1r9uW/B3h+YYoaySRHFY6FvYpmRW+SYzPXl1ddriKSb+3hBHVcHVe477d4YnzppbsGgRrGTqmpHsnl/OjD5nM5lpk2KdG/10w+41dfIWm4/Ld9yyBRCFiFmQV7vZsZbNdxhMx87aJ3JZf69uHG/g5OaLY8K+Lho7qAdflKbeFWC5n83uoaMflwo+Bvp8/lqEcItFtJ1GJrdpZoXfzah5QQmqaoXD/lqvg+7e5O3ul+9XzGyJBRJ96rr+IJzXOQInem2YaMhUha0CIDCM+dntIRq+w/l1OOgHbzKj+gU9OG1iSXt1PCYA+RPZWVdHn4NibU6cu21pLbW63oL/EUiw2pWWm5DbU+k4YEOuwBfGE6BWFpoRU/m3OQ5qYyotlobn09qwg0IYEQtdlk/2acKmi3gCcOVq+ocd3zn69BWRIgOgtg8NGNbMGId1uAbBcJAQyXPrdu3CzPFeAPo80wHxlgiU8In6tafHGmS5hyuTyzhon27fmSGgaPPgvwLKUk12nrPgDoZr/r+W0JRHV3VlHEPHzuECO9Ux4NOrRAtwNHJungYHWzdsokRKTtTkKnS8ULuYc4pgZeAfuPb/1yDwDBE6kGTP9RXYLWXl5aD3q+mJubW5INatN4+Jg44ck6Xis7D/o9NdREQ6rH42sv9bZ+uhtZnGoo0b9Lr5QVz1/6oI1j/s833fX+0NUxkCm4jS6OJI+2vdJoHQCMnDj/AMgtZc877zTuBuVm4WzBVP88yVlXKwyfjhSoQvtxnLbqB6O93Ihs5D1KdRJTPmdyzu1aV+HY5qYQ2TcKfuO2uHZdf/A+jsnRaG8yv7NvPwDgeZevCL12sN5+6RsWsYKTHAyOIkZpCqT1LV+i8e+Zxanf0FHd0zEZt0i6ctm1ZSHLY0zwMj/j8cp8Pq1O6TMfuJdkdxx3WYQJ0vEKXTDXFibNFIzydjYrKNjv/+FxTnZNuonep14+56+MJQA2O20qTafaSdAda0JW3vtuOL7fOcoRKmhUsa25jOuRJmy/uMbm3iLjyLwxDB8BAK0tnP8Zoco0jc4DviYunv9ZQRMFszJZF/wIjOStFe4riWOFGUNzW4WfSKoV1UR/SpxNQsYAjIxjxZSpA1YSIdX9j2ufFV6zRT6TMwNz93sAKm/m2LXGj4xsvJ344+1gR3ZfYyBA8tvfBwdllCVLM5nkQ63MUmZ3rImSjNgWsUMAVFPo+IPiVNICAJoXlJKU7I2yo5amMv1z6/Iy006NiExh0Xn4M13LcJ3J1A6dtNnSIMHG3TNnL/c2ddq/JRIOljNhXZrLhL377vQ0cXebul/PmZzq460Kiijg8FCkAOBNlRi96WaGInADIpJNZGZsYpczOl/gciF+2K6fFVBRHXW7ZC1F0YjL8OBQJXTnv1cATphBYc9ldObrnXLBTsu8pRj1smnUOpw8Y2iNI8evReUtwx7Sl6c7fMGJm3uJ82j48OVslmksMB9mttLudzLS2AXCRCx/09l+MEx79zt7JPfxJgCuOUAL816dCp6tiXhgfel2dkaGdPhehF/BTikOY65cXNwaUiOXDsi33t1479s0ZDoyeKAdcDyoS7kKQKpXa/qlcXkkt+sb4wBHOCJTLP7aKces31NK2PlSO0uMQhyT4AU1cFNEoaB3zo+p7pG+n5Ky8u/mvzwYlapJOtheXROk11djANXLa66kluYiaAAJWdqNAY6k7Q2SxyWn/xN5uFAy/WBYIgAIxQswVxMW4SN+3pbQ2a1Y/x10/+37eVqo7R66jSYIIKpErpSuv/ntkogGh2E0WzZk4UL1xo1txQEMu0PearxjmvqTL5GO1qJMk+G4gxfof3T1lFP4aFHHnCEZrT/amZwjwt7b3EoAcE0we7GsLpI3Fpk8/JPUnt4tWfqNVy3iFr83ZADALBbsX3jzpZcL96im3Jqft8af1EO8QLiSmbMq8bBjZ6ba//azHo5F7YNWR2Hyo7BTV6c5OTtfc5x7/+C8NNU3BD0zxwCzscwB6N57wIXXbRB9vuMJnpvhxqgzwgkjxlPUoYlIz7S2b75RbtxxWJokEs+QEs7CGU8G2+cFUdPO8m9s6JSk9dqUZKPreY4JOwdN4HiCU552s2nw6iOZJMtzppPcf6gB5Kt+0Ca8/OqsFVsw8r2/eGvL0oSLyMdziG0PRNrtrJTkYD8wo/2Z9LF7ez4UPJ5lHCemMxRSp4Msq8Q7fNwsbYmz12aGTR7GW2NALF1J05UH3rlpSohWQUzWNm0BktOSGQGeoTOqMzDkVuvAq3+qb6zteXUAB+fKm85b2Sc50RpXjFHwvW9VTctu3zeyeqm1XK3pgbZnpsbA7BUXJB+Htp2MbEYcUofSgBopUjvtxTcP8Ln6x/lM84de+F/tDtAOSyFVwF7HZu4dj2EikunUVOWz9rkiF9Q/ZKZo9bIvVXdFAtE9IO7lU1bK0ArKNZpkobta0zrhlEBE1QXLyeXreEIOe/9yz7OpGAcaLFMk8trL5iERNN588CTHLJ4vPc5eyzQDdRijr4S7N5r13Olqnrd2uJxZcCD59jjv8ojSPZL21mjAmHe6EheLRCxfr93EE6oFxW2a+oHnkkQStpzTWxpCpzHHhIoqbkNzu98wdijrb+aveAtv/yJ/tmj6jSEFX51ekv1BE41ebnnwztmw2fReu9S/Y19zd3+QdRJR7OJZsmPysGVP8WgzcXQ7PgBCbV1aeJLTiTZANEkpt2X3brMionhNL+dztDNY+gXSwc8Hvd6uY7RpL7HH/8PHzrVzrjdVlLL6ZX0nePdsA89JG0gII1SYWZfd74PZo8ipFRhOqF4lqynZ71tc7e+EIxb/Yn/QL6Kz1fcfh0kaNrqdxCfMWMzRTHl+xn3TALd123OJWqmrVojnKQVoooOMxbUlYhUXg+I5i+HzecksNu+9lxg4ONxHtLaxBdVayQR99dlOpDlXKaALtUy2cjpvl4oiZxCEmw99h/xgFy+mxgPmcFBGWOp95ysfRVmOJ5K7PkKzFq4acSQSjQEA0v2wWop3zEinVEBLZBkGOREUXGIezhOVNh68ZrLZbfxfdJDhR1P6ianXkvv3GZ7qBdmyk+PJEK4IcISysL21EVVzfa0g49SpUAKzImzO6TDBaPt7ampq+3b38qlhjBfjBgP8UI1ubwDgeMqolLiVC4wk4SL0AUBBCq2DdQAqpEQbmmplGtIvCPKPngoT9H98bzV87Tt8cPtdvIik08mO6qewQnwhpy2GJOwJzT+/riKNABP2AuvplBjxIQ9Ftt89wJGBOet+I28U5jbW8AKlS1Pp6JGGefrCjb0E/wtYT0+oEQxnLAAAAABJRU5ErkJggg==");
+ 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 0000000..d2ed30e
Binary files /dev/null and b/static/images/addCart.png differ
diff --git a/static/images/blob-scene-haikei.png b/static/images/blob-scene-haikei.png
new file mode 100644
index 0000000..1bf2cfc
Binary files /dev/null and b/static/images/blob-scene-haikei.png differ
diff --git a/static/images/coupon.png b/static/images/coupon.png
new file mode 100644
index 0000000..131bb97
Binary files /dev/null and b/static/images/coupon.png differ
diff --git a/static/logo.png b/static/logo.png
new file mode 100644
index 0000000..b5771e2
Binary files /dev/null and b/static/logo.png differ
diff --git a/static/style/common.scss b/static/style/common.scss
new file mode 100644
index 0000000..003622f
--- /dev/null
+++ b/static/style/common.scss
@@ -0,0 +1,829 @@
+$bg: #F8F8F8;
+$yellow: #e2d602;
+$orange: #fd7600;
+$orange-deep: #FF4B00;
+$orange-light: #FFC5AD;
+$green: #2fc6a7;
+$green-light: #a1ded0;
+$blue: #2563eb;
+$blue-light: #049DFF;
+$blue-light-50: #ebf3f8;
+$purple: #c770f6;
+$purple-light: #E0AFFB;
+$red: #FE6869;
+$red-light: #F7B2B2;
+$gray-weight: #666;
+$gray: #bbb;
+$gray-light: #F7F7F7;
+$gold: #B68320;
+$gold-light: #FFF0D3;
+$fdca00: #FDCA00;
+$u-primary: $green;
+
+//p, span {
+// font-size: 30rpx;
+// margin: 0;
+//}
+
+h6 {
+ font-weight: bolder !important;
+ font-size: 20rpx;
+}
+
+.pos-r {
+ position: relative;
+}
+
+.pos-a {
+ position: absolute;
+}
+
+.bg {
+ background-color: $bg;
+}
+
+.bg-gray-light {
+ background-color: $gray-light;
+}
+
+.bg-white {
+ background-color: white;
+}
+
+.bg-main {
+ background-color: $u-primary;
+}
+
+.bg-linear {
+ background : linear-gradient(90deg, #049DFF, #2665c4);
+}
+
+.bg-red {
+ background-color: $red;
+}
+
+.bg-orange {
+ background-color: $orange;
+}
+
+.bg-blue {
+ background-color: $blue;
+}
+
+.bg-gold {
+ background-color: $gold;
+}
+
+.bg-green {
+ background-color: $green;
+}
+
+.bg-main-light {
+ background-color: #E0F2FE;
+}
+
+.bg-fff0 {
+ background-color: #F0F0F0FF;
+}
+
+.shadow {
+ box-shadow: 0 2px 6px 0 rgba(191, 218, 251, 1);
+}
+
+.shadow-top {
+ box-shadow: 0px -2px 13px rgba(191, 218, 251, 1);
+}
+
+.shadow-light {
+ box-shadow: 0 4rpx 12rpx 0 rgba(206, 206, 206, 80);
+}
+
+.shadow-50 {
+ box-shadow: -2rpx 4rpx 14rpx 4rpx rgba(206, 206, 206, 50);
+}
+
+.font-bold {
+ font-weight: bold;
+}
+
+.text-center {
+ text-align: center;
+}
+
+.text-left {
+ text-align: left;
+}
+
+.text-right {
+ text-align: right;
+}
+
+.tips {
+ font-size: 20rpx;
+ color: $gray;
+}
+
+.text-main {
+ color: $u-primary !important;
+}
+
+.text-orange {
+ color: #fd7600;
+}
+
+.text-blue {
+ color: $blue;
+}
+
+.text-blue-light {
+ color: $blue-light;
+}
+
+.text-green {
+ color: $green;
+}
+
+.text-red {
+ color: $red;
+}
+
+.text-gray {
+ color: $gray;
+}
+
+.text-gray-weight {
+ color: $gray-weight;
+}
+
+.text-black {
+ color: #333;
+}
+
+.color-6C {
+ color: #6C6C6C;
+}
+
+.text-white {
+ color: white;
+}
+
+.text-line-through {
+ text-decoration: line-through;
+}
+
+.color-9A {
+ color: #9A9A9A;
+}
+
+.color-333 {
+ color: #333333;
+}
+
+.flex {
+ display: flex;
+}
+
+.flex-wrap {
+ flex-wrap: wrap;
+}
+
+.justify-center {
+ justify-content: center;
+}
+
+.justify-start {
+ justify-content: flex-start;
+}
+
+.justify-end {
+ justify-content: flex-end;
+}
+
+.justify-between {
+ justify-content: space-between;
+}
+
+.justify-around {
+ justify-content: space-around;
+}
+
+.items-center {
+ align-items: center;
+}
+
+.items-start {
+ align-items: flex-start;
+}
+
+.items-end {
+ align-items: flex-end;
+}
+
+.item-self {
+ align-self: flex-end;
+}
+
+.flex-col {
+ flex-direction: column;
+}
+
+.flex-rev {
+ flex-direction: row-reverse;
+}
+
+.flex-1 {
+ flex: 1;
+}
+
+.flex-2 {
+ flex: 2;
+}
+
+.flex-shrink {
+ flex-shrink: 0;
+}
+
+.flex-none {
+ flex: none;
+}
+
+.flex-grow {
+ flex-grow: 1
+}
+
+.flex-grow-0 {
+ flex-grow: 0
+}
+
+.h-100 {
+ height: 100vh;
+}
+
+.h-100p {
+ height: 100%;
+}
+
+.h-90p {
+ height: 90%;
+}
+
+.h-50p {
+ height: 50%;
+}
+
+.h-10p {
+ height: 10%;
+}
+
+.w-100 {
+ width: 100vw;
+}
+
+.w-100p {
+ width: 100%;
+}
+
+.w-5p {
+ width: 5%;
+}
+
+.w-10p {
+ width: 10%;
+}
+
+.w-15p {
+ width: 15%;
+}
+
+.w-20p {
+ width: 20%;
+}
+
+.w-25p {
+ width: 25%;
+}
+
+.w-30p {
+ width: 30%;
+}
+
+.w-33p {
+ width: 33%;
+}
+
+.w-40p {
+ width: 40%;
+}
+
+.w-45p {
+ width: 45%;
+}
+
+.w-50p {
+ width: 50%;
+}
+
+.w-60p {
+ width: 60%;
+}
+
+.w-67p {
+ width: 67%;
+}
+
+.w-75p {
+ width: 75%;
+}
+
+.w-80p {
+ width: 80%;
+}
+
+.w-85p {
+ width: 85%;
+}
+
+.w-90p {
+ width: 90%;
+}
+
+.p-10 {
+ padding: 10rpx;
+}
+
+.p-15 {
+ padding: 15rpx;
+}
+
+.pb-15 {
+ padding-bottom: 15rpx;
+}
+
+.pb-20 {
+ padding-bottom: 20rpx;
+}
+
+.pb-30 {
+ padding-bottom: 30rpx;
+}
+
+.p-20 {
+ padding: 20rpx;
+}
+
+.p-25 {
+ padding: 25rpx;
+}
+
+.p-30 {
+ padding: 30rpx;
+}
+
+.px-20 {
+ padding: 0 20rpx;
+}
+
+.px-40 {
+ padding: 0 40rpx;
+}
+
+.p-50 {
+ padding: 50rpx;
+}
+
+.py-10 {
+ padding: 10rpx 0;
+}
+
+.py-15 {
+ padding: 15rpx 0;
+}
+
+.py-20 {
+ padding: 20rpx 0;
+}
+
+.py-25 {
+ padding: 25rpx 0;
+}
+
+.py-30 {
+ padding: 30rpx 0;
+}
+
+.px-15 {
+ padding: 0 15rpx;
+}
+
+.px-20 {
+ padding: 0 15rpx;
+}
+
+.px-30 {
+ padding: 0 30rpx;
+}
+
+.pl-15 {
+ padding-left: 15rpx;
+}
+
+.pl-20 {
+ padding-left: 20rpx;
+}
+
+.pr-15 {
+ padding-right: 15rpx;
+}
+
+.pr-20 {
+ padding-right: 20rpx;
+}
+
+.pt-10 {
+ padding-top: 10rpx;
+}
+
+.pt-20 {
+ padding-top: 20rpx;
+}
+
+.pr-25 {
+ padding-right: 25rpx;
+}
+
+.m-a {
+ margin: auto;
+}
+
+.m-10 {
+ margin: 10rpx;
+}
+
+.m-15 {
+ margin: 15rpx;
+}
+
+.m-20 {
+ margin: 20rpx;
+}
+
+.m-25 {
+ margin: 25rpx;
+}
+
+.m-30 {
+ margin: 30rpx;
+}
+
+.m-35 {
+ margin: 35rpx;
+}
+
+.ml-10 {
+ margin-left: 10rpx;
+}
+
+.ml-15 {
+ margin-left: 15rpx;
+}
+
+.ml-25 {
+ margin-left: 25rpx;
+}
+
+.ml-35 {
+ margin-left: 35rpx;
+}
+
+.mt-10 {
+ margin-top: 10rpx;
+}
+
+.mt-15 {
+ margin-top: 15rpx;
+}
+
+.mt-20 {
+ margin-top: 20rpx;
+}
+
+.mt-25 {
+ margin-top: 25rpx;
+}
+
+.mt-30 {
+ margin-top: 30rpx;
+}
+
+.mt-40 {
+ margin-top: 40rpx;
+}
+
+.mt-50 {
+ margin-top: 50rpx;
+}
+
+.mb-25 {
+ margin-bottom: 25rpx;
+}
+
+.mb-40 {
+ margin-bottom: 40rpx;
+}
+
+.my-10 {
+ margin-top: 10rpx;
+ margin-bottom: 10rpx;
+}
+
+.my-15 {
+ margin-top: 15rpx;
+ margin-bottom: 15rpx;
+}
+
+.my-20 {
+ margin-top: 20rpx;
+ margin-bottom: 20rpx;
+}
+
+.my-25 {
+ margin-top: 25rpx;
+ margin-bottom: 25rpx;
+}
+
+.mx-15 {
+ margin-left: 15rpx;
+ margin-right: 15rpx;
+}
+
+.mx-20 {
+ margin-left: 20rpx;
+ margin-right: 20rpx;
+}
+
+.mx-25 {
+ margin-left: 25rpx;
+ margin-right: 25rpx;
+}
+
+.mb-10 {
+ margin-bottom: 10rpx;
+}
+
+.mb-15 {
+ margin-bottom: 15rpx;
+}
+
+.mb-20 {
+ margin-bottom: 20rpx;
+}
+
+.mr-10 {
+ margin-right: 10rpx;
+}
+
+.mr-15 {
+ margin-right: 15rpx;
+}
+
+.ml-20 {
+ margin-left: 20rpx;
+}
+
+.mr-25 {
+ margin-right: 25rpx;
+}
+
+.mr-35 {
+ margin-right: 35rpx;
+}
+
+.text-15 {
+ font-size: 15rpx;
+}
+
+.text-20 {
+ font-size: 20rpx;
+ line-height: 20rpx;
+}
+
+.text-24 {
+ font-size: 24rpx;
+}
+
+.text-25 {
+ font-size: 25rpx;
+ line-height: 35rpx;
+}
+
+.text-26 {
+ font-size: 26rpx;
+}
+
+.text-28 {
+ font-size: 28rpx;
+ line-height: 28rpx;
+}
+
+.text-30 {
+ font-size: 30rpx;
+ line-height: 30rpx;
+}
+
+.text-35 {
+ font-size: 35rpx;
+ line-height: 35rpx;
+}
+
+.text-40 {
+ font-size: 40rpx;
+ line-height: 40rpx;
+}
+
+.rounded {
+ border-radius: 40rpx;
+}
+
+.rounded-sm {
+ border-radius: 20rpx;
+}
+
+.circle {
+ border-radius: 999px;
+}
+
+.rounded-16 {
+ border-radius: 16rpx;
+}
+
+.rounded-bottom {
+ border-radius: 0 0 40rpx 40rpx;
+}
+
+.rounded-top {
+ border-radius: 40rpx 40rpx 0 0;
+}
+
+.border-top {
+ border-top: 1px solid $gray-light;
+}
+
+.border-bottom {
+ border-bottom: 1px solid $gray-light;
+}
+
+
+.border-right {
+ border-right: 1px solid $gray-light;
+}
+
+.border {
+ border: 1px solid $gray;
+}
+
+.canvas-hide {
+ //position: fixed;
+ //right: 100vw;
+ //bottom: 100vh;
+ //z-index: -9999;
+ //opacity: 0;
+}
+
+.card {
+ @extend .rounded;
+ @extend .bg-white;
+ @extend .p-20;
+}
+
+.footer {
+ position: fixed;
+ left: 0;
+ bottom: 0;
+ @extend .w-100p;
+ z-index: 10;
+}
+
+.public-sub-btn {
+ background: $fdca00;
+ font-weight: bold;
+ color: #000;
+ border-radius: 50rpx;
+ text-align: center;
+ padding: 24rpx 0;
+}
+
+.b-20 {
+ bottom: 20rpx;
+}
+
+.p-button {
+ background: linear-gradient(#E5CB98, #FBEDBC);
+ color: #B68320;
+}
+
+@font-face {
+ font-family: "iconfont";
+ src: url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2');
+}
+
+.iconfont {
+ flex: 1;
+ text-align: center;
+ font-family: "iconfont" !important;
+ font-size: 22px;
+ font-style: normal;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+.icon-undo:before {
+ content: "\e607";
+}
+
+.icon-redo:before {
+ content: "\e606";
+}
+
+.icon-img:before {
+ content: "\e6e2";
+}
+
+.icon-video:before {
+ content: "\e798";
+}
+
+.icon-link:before {
+ content: "\e60d";
+}
+
+.icon-text:before {
+ content: "\e6ce";
+}
+
+.icon-clear:before {
+ content: "\e637";
+}
+
+.icon-save:before {
+ content: "\e501";
+}
+
+.required {
+ @extend .pos-a;
+ left: -9px;
+ color: #f56c6c;
+ line-height: 20px;
+ font-size: 20px;
+ top: 15px;
+}
+
+.overflow-y {
+ overflow-y: auto;
+}
+
+.overflow-x {
+ overflow-x: auto;
+}
+
+.album-item {
+ width: 190rpx;
+ height: 190rpx;
+ @extend .m-10;
+ @extend .flex;
+ @extend .justify-center;
+ @extend .items-center;
+}
+
+.del-wrap {
+ @extend .pos-a;
+ right: 0;
+ top: 0;
+ border-radius: 0 0 0 5px;
+ padding: 10rpx;
+ background-color: $red;
+ z-index: 1000;
+}
+
+.min-height {
+ min-height: 100vh;
+}
+
+.share-btn {
+ border: none;
+ background: none;
+ margin: 0;
+ padding: 0;
+
+ &::after {
+ border: none;
+ }
+}
+
+.border-main{
+ border-color: $u-primary !important;
+}
+
+.popup-close {
+ bottom: -80rpx;
+ left: calc(50% - 20rpx);
+}
\ No newline at end of file
diff --git a/static/tabbar/group-active.png b/static/tabbar/group-active.png
new file mode 100644
index 0000000..1946992
Binary files /dev/null and b/static/tabbar/group-active.png differ
diff --git a/static/tabbar/group.png b/static/tabbar/group.png
new file mode 100644
index 0000000..556c1ed
Binary files /dev/null and b/static/tabbar/group.png differ
diff --git a/static/tabbar/home-active.png b/static/tabbar/home-active.png
new file mode 100644
index 0000000..778ac35
Binary files /dev/null and b/static/tabbar/home-active.png differ
diff --git a/static/tabbar/home.png b/static/tabbar/home.png
new file mode 100644
index 0000000..cdef977
Binary files /dev/null and b/static/tabbar/home.png differ
diff --git a/static/tabbar/share-active.png b/static/tabbar/share-active.png
new file mode 100644
index 0000000..d337676
Binary files /dev/null and b/static/tabbar/share-active.png differ
diff --git a/static/tabbar/share.png b/static/tabbar/share.png
new file mode 100644
index 0000000..0fd2324
Binary files /dev/null and b/static/tabbar/share.png differ
diff --git a/static/tabbar/switch-active.png b/static/tabbar/switch-active.png
new file mode 100644
index 0000000..6358094
Binary files /dev/null and b/static/tabbar/switch-active.png differ
diff --git a/static/tabbar/switch.png b/static/tabbar/switch.png
new file mode 100644
index 0000000..813935c
Binary files /dev/null and b/static/tabbar/switch.png differ
diff --git a/static/tabbar/user-active.png b/static/tabbar/user-active.png
new file mode 100644
index 0000000..3b8d519
Binary files /dev/null and b/static/tabbar/user-active.png differ
diff --git a/static/tabbar/user.png b/static/tabbar/user.png
new file mode 100644
index 0000000..8a96fd2
Binary files /dev/null and b/static/tabbar/user.png differ
diff --git a/uni.promisify.adaptor.js b/uni.promisify.adaptor.js
new file mode 100644
index 0000000..5fec4f3
--- /dev/null
+++ b/uni.promisify.adaptor.js
@@ -0,0 +1,13 @@
+uni.addInterceptor({
+ returnValue (res) {
+ if (!(!!res && (typeof res === "object" || typeof res === "function") && typeof res.then === "function")) {
+ return res;
+ }
+ return new Promise((resolve, reject) => {
+ res.then((res) => {
+ if (!res) return resolve(res)
+ return res[0] ? reject(res[0]) : resolve(res[1])
+ });
+ });
+ },
+});
\ No newline at end of file
diff --git a/uni.scss b/uni.scss
new file mode 100644
index 0000000..e3d2b3c
--- /dev/null
+++ b/uni.scss
@@ -0,0 +1,77 @@
+/**
+ * 这里是uni-app内置的常用样式变量
+ *
+ * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
+ * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
+ *
+ */
+
+/**
+ * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
+ *
+ * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
+ */
+
+/* 颜色变量 */
+@import '@/uni_modules/uview-ui/theme.scss';
+@import "@/static/style/common.scss";
+/* 行为相关颜色 */
+$uni-color-primary: #2563eb;
+$uni-color-success: #4cd964;
+$uni-color-warning: #f0ad4e;
+$uni-color-error: #dd524d;
+
+/* 文字基本颜色 */
+$uni-text-color:#333;//基本色
+$uni-text-color-inverse:#fff;//反色
+$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
+$uni-text-color-placeholder: #808080;
+$uni-text-color-disable:#c0c0c0;
+
+/* 背景颜色 */
+$uni-bg-color:#ffffff;
+$uni-bg-color-grey:#f8f8f8;
+$uni-bg-color-hover:#f1f1f1;//点击状态颜色
+$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
+
+/* 边框颜色 */
+$uni-border-color:#c8c7cc;
+
+/* 尺寸变量 */
+
+/* 文字尺寸 */
+$uni-font-size-sm:12px;
+$uni-font-size-base:14px;
+$uni-font-size-lg:16px;
+
+/* 图片尺寸 */
+$uni-img-size-sm:20px;
+$uni-img-size-base:26px;
+$uni-img-size-lg:40px;
+
+/* Border Radius */
+$uni-border-radius-sm: 2px;
+$uni-border-radius-base: 3px;
+$uni-border-radius-lg: 6px;
+$uni-border-radius-circle: 50%;
+
+/* 水平间距 */
+$uni-spacing-row-sm: 5px;
+$uni-spacing-row-base: 10px;
+$uni-spacing-row-lg: 15px;
+
+/* 垂直间距 */
+$uni-spacing-col-sm: 4px;
+$uni-spacing-col-base: 8px;
+$uni-spacing-col-lg: 12px;
+
+/* 透明度 */
+$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
+
+/* 文章场景相关 */
+$uni-color-title: #2C405A; // 文章标题颜色
+$uni-font-size-title:20px;
+$uni-color-subtitle: #555555; // 二级标题颜色
+$uni-font-size-subtitle:26px;
+$uni-color-paragraph: #3F536E; // 文章段落颜色
+$uni-font-size-paragraph:15px;
diff --git a/util/index.js b/util/index.js
new file mode 100644
index 0000000..3dab220
--- /dev/null
+++ b/util/index.js
@@ -0,0 +1,292 @@
+import {getLocation, getUserInfo, setLocation} from "@/util/user";
+import {API, MAIN_API} from "@/config";
+import {get} from "@/api/request";
+import {areaInfoByNameReq, areaInfoReq} from "@/api/common";
+import dayjs from "dayjs";
+import md5 from "md5";
+
+export const ORDER_STATUS_LIST = ['已取消', '进行中', '已完成', '申请退款中', '退款完成', '退款失败']
+
+export const CHECK_STATUS_LIST = [
+ {
+ label: '审核中',
+ color: 'info'
+ },
+ {
+ label: '审核通过',
+ color: 'success'
+ },
+ {
+ label: '审核拒绝',
+ color: 'error'
+ },
+ {
+ label: '已到账',
+ color: 'success'
+ },
+
+]
+
+export const chooseImg = (limit = 1, sourceType = ['album', 'camera']) => {
+ return new Promise(resolveMain => {
+ uni.chooseImage({
+ count: limit,
+ sourceType,
+ extension: ['png', 'jpg', 'jpeg'],
+ sizeType: ['original'],
+ success: async ({tempFiles}) => {
+ const result = await Promise.all(tempFiles.map(async file => {
+ const uploadRes = await uploadFileToAliOss(file.path)
+ return {
+ path: uploadRes,
+ }
+ }))
+ resolveMain(result)
+ },
+ complete: () => {
+ uni.hideLoading()
+ }
+ })
+ })
+}
+
+let ossUploadSeed = 0
+
+const createOssKey = (filePath) => {
+ ossUploadSeed += 1
+ const suffix = filePath.substring(filePath.lastIndexOf("."));
+ const hash = md5(`${filePath}_${Date.now()}_${ossUploadSeed}_${Math.random()}`)
+ return `${dayjs().format('YYYYMMDD')}/${hash}${suffix}`
+}
+
+export const uploadImg = filePath => {
+ const {token} = getUserInfo()
+ uni.showLoading({title: '上传中'})
+ return new Promise(async resolve => {
+ // uni.uploadFile({
+ // url: `${MAIN_API}/api/file/upload`,
+ // name: 'file',
+ // filePath,
+ // header: {
+ // Authorization: token,
+ // TenantId: 10325
+ // },
+ // formData: {
+ // type: 'header'
+ // },
+ // success: uploadRes => {
+ // uni.hideLoading()
+ // const {data: {url}} = JSON.parse(uploadRes.data)
+ // resolve({
+ // path: url,
+ // })
+ // }
+ // })
+ const uploadRes = await uploadFileToAliOss(filePath)
+ uni.hideLoading()
+ resolve({
+ path: uploadRes,
+ })
+ })
+}
+
+const getSts = () => get('/oss/getSTSToken')
+
+export const uploadFileToAliOss = async (filePath) => {
+ const stsRes = await getSts()
+ const sts = stsRes.data
+ const key = createOssKey(filePath)
+ const url = 'https://yaodongxiao.oss-cn-guangzhou.aliyuncs.com';
+ return new Promise(resolve => {
+ uni.uploadFile({
+ url,
+ name: 'file',
+ filePath,
+ formData: {
+ key,
+ OSSAccessKeyId: sts.accessKeyId,
+ signature: sts.signature,
+ security_token: sts.token,
+ policy: sts.policy,
+ },
+ success: () => {
+ resolve(`${url}/${key}`)
+ }
+ })
+ })
+}
+
+
+export const getLocationAction = (save = true) => {
+ return new Promise((resolve, reject) => {
+ const res = getLocation()
+ if (res.area_id) resolve(res)
+ else {
+ uni.authorize({
+ scope: 'scope.userLocation',
+ success: () => {
+ uni.getLocation({
+ isHighAccuracy: true,
+ success: async ({longitude, latitude}) => {
+ const {result} = await get(`https://api.tianditu.gov.cn/geocoder?postStr={'lon':${longitude},'lat':${latitude},'ver':1}&type=geocode&tk=1099ec280e86f4e9b632173326cde90b`)
+ const areaRes = await areaInfoByNameReq({name: result.addressComponent.county}).catch(() => {
+ if (!this.areaId) reject(false)
+ })
+ if (save) setLocation({
+ longitude,
+ latitude,
+ province_str: result.addressComponent.province,
+ city_str: result.addressComponent.city,
+ area_str: result.addressComponent.county,
+ area_id: areaRes.data.id,
+ city_id: areaRes.data.fid,
+ province_id: areaRes.data.city.fid
+ })
+ resolve({
+ longitude,
+ latitude,
+ area_id: areaRes.data.id,
+ city_id: areaRes.data.fid,
+ province_id: areaRes.data.city.fid,
+ province_str: result.addressComponent.province,
+ city_str: result.addressComponent.city,
+ area_str: result.addressComponent.county,
+ })
+ },
+ fail: () => {
+ reject(false)
+ }
+ })
+ },
+ fail: () => {
+ uni.showModal({
+ title: '提示',
+ content: '为了更好的提供服务,请开启定位服务',
+ success: ({confirm}) => {
+ if (confirm) uni.openSetting()
+ }
+ })
+ }
+ })
+ }
+ })
+}
+
+export const chooseVideo = (sourceType = ['album', 'camera']) => {
+ return new Promise((resolveMain, reject) => {
+ uni.chooseVideo({
+ sourceType,
+ compressed: true,
+ maxDuration: 60,
+ success: async ({tempFilePath, duration, size}) => {
+ if (duration > 180) {
+ uni.showToast({
+ title: '暂时不支持3分钟以上的视频',
+ icon: 'none'
+ })
+ return reject(false)
+ }
+ // 50M限制
+ if (size > 50 * 1024 * 1024) {
+ uni.showToast({
+ title: '暂时不支持50M以上的视频',
+ icon: 'none'
+ })
+ return reject(false)
+ }
+ const {token} = getUserInfo()
+ uni.showLoading({title: '上传中...'})
+ // uni.uploadFile({
+ // url: `${MAIN_API}/api/file/upload`,
+ // name: 'file',
+ // filePath: tempFilePath,
+ // header: {
+ // Authorization: token,
+ // TenantId: 10325
+ // },
+ // success: uploadRes => {
+ // const {data} = JSON.parse(uploadRes.data)
+ // uni.hideLoading()
+ // resolveMain({
+ // path: data.url,
+ // })
+ // }
+ // })
+ const uploadRes = await uploadFileToAliOss(tempFilePath)
+ resolveMain({
+ path: uploadRes,
+ })
+ }
+ })
+ })
+}
+
+export const getLocationOnly = () => {
+ return new Promise((resolve, reject) => {
+ uni.authorize({
+ scope: 'scope.userLocation',
+ success: () => {
+ uni.getLocation({
+ isHighAccuracy: true,
+ success: async ({longitude, latitude}) => {
+ const {result} = await get(`https://api.tianditu.gov.cn/geocoder?postStr={'lon':${longitude},'lat':${latitude},'ver':1}&type=geocode&tk=1099ec280e86f4e9b632173326cde90b`)
+ const {data} = await areaInfoByNameReq({name: result.addressComponent.county}).catch(() => {
+ if (!this.areaId) reject(false)
+ })
+ resolve({
+ longitude,
+ latitude,
+ area_id: data.id,
+ city_id: data.fid,
+ province_id: data.city.fid,
+ province_str: result.addressComponent.province,
+ city_str: result.addressComponent.city,
+ area_str: result.addressComponent.county,
+ full: result.formatted_address
+ })
+ },
+ fail: () => {
+ uni.setStorageSync("no_more_realtime_location", 1)
+ reject(false)
+ }
+ })
+ },
+ fail: err => {
+ uni.showModal({
+ title: '提示',
+ content: '为了更好的提供服务,请开启定位服务',
+ success: ({confirm}) => {
+ if (confirm) uni.openSetting()
+ }
+ })
+ }
+ })
+ })
+}
+
+
+export const getExpressMode = () => parseInt(uni.getStorageSync("express_mode"))
+
+export const calcDistanceMeters = (lat1, lng1, lat2, lng2) => {
+ const nLat1 = Number(lat1)
+ const nLng1 = Number(lng1)
+ const nLat2 = Number(lat2)
+ const nLng2 = Number(lng2)
+ if (![nLat1, nLng1, nLat2, nLng2].every(Number.isFinite)) return NaN
+ const rad = value => value * Math.PI / 180
+ const r = 6371000
+ const dLat = rad(nLat2 - nLat1)
+ const dLng = rad(nLng2 - nLng1)
+ const a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
+ + Math.cos(rad(nLat1)) * Math.cos(rad(nLat2))
+ * Math.sin(dLng / 2) * Math.sin(dLng / 2)
+ const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
+ return r * c
+}
+
+export const formatDistance = meters => {
+ if (!Number.isFinite(meters)) return ''
+ if (meters < 1000) return `${Math.round(meters)}m`
+ const km = (meters / 1000).toFixed(1).replace(/\.0$/, '')
+ return `${km}km`
+}
diff --git a/util/order.js b/util/order.js
new file mode 100644
index 0000000..343321c
--- /dev/null
+++ b/util/order.js
@@ -0,0 +1,100 @@
+import {BLUE, BLUE_LIGHT, GREEN, ORANGE, RED} from "@/config/color";
+
+// -1已取消、0待付款、1待发货/配送中、2待收货、3已完成、4退款中、5退款成功、6退款失败
+
+export const ORDER_STATUS_LIST = [
+ {
+ label: '已取消',
+ value: -1,
+ color: '#BBB',
+ },
+ {
+ label: '待付款',
+ value: 0,
+ color: RED,
+ },
+ {
+ label: '配送中',
+ value: 1,
+ color: BLUE_LIGHT,
+ },
+ {
+ label: '待收货',
+ value: 2,
+ color: BLUE,
+ },
+ {
+ label: '已完成',
+ value: 3,
+ color: GREEN,
+ },
+ {
+ label: '退款中',
+ value: 4,
+ color: '#F9A8D4',
+ },
+ {
+ label: '退款成功',
+ value: 5,
+ color: '#DB2777',
+ },
+ {
+ label: '退款失败',
+ value: 6,
+ color: '#831843',
+ },
+]
+
+export const EXPRESS_ORDER_STATUS_LIST = [
+ {
+ label: '已取消',
+ value: -1,
+ color: '#BBB',
+ },
+ {
+ label: '待支付',
+ value: 0,
+ color: RED,
+ },
+ {
+ label: '待接单',
+ value: 1,
+ color: BLUE_LIGHT,
+ },
+ {
+ label: '配送中',
+ value: 2,
+ color: BLUE,
+ },
+ {
+ label: '待确认',
+ value: 20,
+ color: BLUE,
+ },
+ {
+ label: '已完成',
+ value: 3,
+ color: GREEN,
+ },
+ {
+ label: '退款中',
+ value: 4,
+ color: '#F9A8D4',
+ },
+ {
+ label: '退款成功',
+ value: 5,
+ color: '#DB2777',
+ },
+ {
+ label: '退款失败',
+ value: 6,
+ color: '#831843',
+ },
+]
+
+export const getOrderStatusItem = (type, orderStatus) => {
+ if ([1, 2, 3, 4, 5].includes(type))
+ return EXPRESS_ORDER_STATUS_LIST.filter(item => item.value === orderStatus)[0]
+ else return ORDER_STATUS_LIST.filter(item => item.value === orderStatus)[0]
+}
\ No newline at end of file
diff --git a/util/user.js b/util/user.js
new file mode 100644
index 0000000..336bfe8
--- /dev/null
+++ b/util/user.js
@@ -0,0 +1,129 @@
+import {MAIN_API} from "@/config";
+
+const SHARE_USER_ID_KEY = "share_user_id"
+
+const normalizeStorageValue = (value) => {
+ if (value === undefined || value === null) return ""
+ const text = String(value).trim()
+ if (!text || text === "undefined" || text === "null") return ""
+ return text
+}
+
+const parsePathQuery = (path = "") => {
+ const [pathname = "", queryString = ""] = String(path).split("?")
+ const query = {}
+ if (!queryString) return {pathname, query}
+ queryString.split("&").forEach(segment => {
+ if (!segment) return
+ const [rawKey = "", rawValue = ""] = segment.split("=")
+ const key = decodeURIComponent(rawKey)
+ if (!key) return
+ query[key] = decodeURIComponent(rawValue)
+ })
+ return {pathname, query}
+}
+
+const buildPathQuery = (pathname = "", query = {}) => {
+ const queryString = Object.keys(query)
+ .filter(key => normalizeStorageValue(key) && normalizeStorageValue(query[key]) !== "")
+ .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(query[key])}`)
+ .join("&")
+ return queryString ? `${pathname}?${queryString}` : pathname
+}
+
+export const setLocation = ({latitude, longitude, province_str, city_str, area_str, area_id, city_id, province_id}) => {
+ if (latitude) uni.setStorageSync("latitude", latitude)
+ if (longitude) uni.setStorageSync("longitude", longitude)
+ uni.setStorageSync("province_str", province_str)
+ uni.setStorageSync("city_str", city_str)
+ uni.setStorageSync("area_str", area_str)
+ uni.setStorageSync("area_id", area_id)
+ uni.setStorageSync("city_id", city_id)
+ uni.setStorageSync("province_id", province_id)
+}
+
+export const getLocation = () => {
+ return {
+ latitude: uni.getStorageSync("latitude"),
+ longitude: uni.getStorageSync("longitude"),
+ province_str: uni.getStorageSync("province_str"),
+ city_str: uni.getStorageSync("city_str"),
+ area_str: uni.getStorageSync("area_str"),
+ area_id: uni.getStorageSync("area_id"),
+ city_id: uni.getStorageSync("city_id"),
+ province_id: uni.getStorageSync("province_id")
+ }
+}
+
+export const setUserInfo = (user) => {
+ if (user.uid) uni.setStorageSync("uid", user.uid)
+ if (user.username) uni.setStorageSync("username", user.username)
+ if (user.token) uni.setStorageSync("token", user.token)
+ if (user.header) uni.setStorageSync("header", `${MAIN_API}${user.header}`)
+ if (user.phone) uni.setStorageSync("phone", user.phone)
+}
+
+export const getUserInfo = () => {
+ return {
+ uid: uni.getStorageSync("uid"),
+ username: uni.getStorageSync("username"),
+ token: uni.getStorageSync("token"),
+ header: uni.getStorageSync("header"),
+ phone: uni.getStorageSync("phone"),
+ }
+}
+
+export const clearUserInfo = () => {
+ uni.removeStorageSync("uid");
+ uni.removeStorageSync("username");
+ uni.removeStorageSync("token");
+ uni.removeStorageSync("header");
+ uni.removeStorageSync("phone");
+}
+
+export const getShareUserId = () => normalizeStorageValue(uni.getStorageSync(SHARE_USER_ID_KEY))
+
+export const setShareUserId = (userId) => {
+ const value = normalizeStorageValue(userId)
+ if (!value) return ""
+ uni.setStorageSync(SHARE_USER_ID_KEY, value)
+ return value
+}
+
+export const clearShareUserId = () => {
+ uni.removeStorageSync(SHARE_USER_ID_KEY)
+}
+
+export const saveShareUserIdFromQuery = (query = {}) => {
+ const shareUserId = normalizeStorageValue(
+ query.shareUserId ?? query.inviterUserId ?? query.userId ?? query.uid
+ )
+ if (!shareUserId) return ""
+ const currentUserId = normalizeStorageValue(uni.getStorageSync("uid"))
+ if (currentUserId && currentUserId === shareUserId) return ""
+ return setShareUserId(shareUserId)
+}
+
+export const appendPathParams = (path = "", params = {}) => {
+ const {pathname, query} = parsePathQuery(path)
+ Object.keys(params).forEach(key => {
+ const value = normalizeStorageValue(params[key])
+ if (value === "") return
+ query[key] = value
+ })
+ return buildPathQuery(pathname, query)
+}
+
+export const getCurrentPagePath = () => {
+ const pages = typeof getCurrentPages === "function" ? getCurrentPages() : []
+ const currentPage = pages[pages.length - 1]
+ if (!currentPage || !currentPage.route) return ""
+ return appendPathParams(`/${currentPage.route}`, currentPage.options || {})
+}
+
+export const buildSharePath = (path = "") => {
+ const shareUserId = normalizeStorageValue(uni.getStorageSync("uid"))
+ const targetPath = normalizeStorageValue(path) || getCurrentPagePath()
+ if (!targetPath || !shareUserId) return targetPath
+ return appendPathParams(targetPath, {shareUserId})
+}