feat: 接口联调
This commit is contained in:
parent
beded95be0
commit
5f446bc97a
8
components.d.ts
vendored
8
components.d.ts
vendored
@ -10,23 +10,15 @@ declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
VanArea: typeof import('vant/es')['Area']
|
||||
VanButton: typeof import('vant/es')['Button']
|
||||
VanCell: typeof import('vant/es')['Cell']
|
||||
VanCellGroup: typeof import('vant/es')['CellGroup']
|
||||
VanCheckbox: typeof import('vant/es')['Checkbox']
|
||||
VanCheckboxGroup: typeof import('vant/es')['CheckboxGroup']
|
||||
VanField: typeof import('vant/es')['Field']
|
||||
VanForm: typeof import('vant/es')['Form']
|
||||
VanIcon: typeof import('vant/es')['Icon']
|
||||
VanNoticeBar: typeof import('vant/es')['NoticeBar']
|
||||
VanPopover: typeof import('vant/es')['Popover']
|
||||
VanPopup: typeof import('vant/es')['Popup']
|
||||
VanRate: typeof import('vant/es')['Rate']
|
||||
VanSearch: typeof import('vant/es')['Search']
|
||||
VanTab: typeof import('vant/es')['Tab']
|
||||
VanTabs: typeof import('vant/es')['Tabs']
|
||||
VanTag: typeof import('vant/es')['Tag']
|
||||
VanUploader: typeof import('vant/es')['Uploader']
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ import { globalIgnores } from 'eslint/config'
|
||||
import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript'
|
||||
import pluginVue from 'eslint-plugin-vue'
|
||||
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting'
|
||||
const path = require('path')
|
||||
|
||||
// To allow more languages other than `ts` in `.vue` files, uncomment the following lines:
|
||||
// import { configureVueProject } from '@vue/eslint-config-typescript'
|
||||
@ -16,6 +17,8 @@ export default defineConfigWithVueTs(
|
||||
{
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
sconfigRootDir: path.resolve(__dirname),
|
||||
project: './tsconfig.json',
|
||||
requireConfigFile: false,
|
||||
},
|
||||
},
|
||||
|
||||
@ -18,8 +18,12 @@
|
||||
"dependencies": {
|
||||
"@vant/area-data": "^2.1.0",
|
||||
"axios": "^1.11.0",
|
||||
"capture-request-log": "^0.0.4",
|
||||
"js-base64": "^3.7.8",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash-es": "^4.17.21",
|
||||
"lz-utils-lib": "^1.0.60",
|
||||
"mobile-detect": "^1.4.5",
|
||||
"normalize.css": "^8.0.1",
|
||||
"pinia": "^3.0.3",
|
||||
"pinia-plugin-persistedstate": "^4.5.0",
|
||||
|
||||
8
src/api/commodity.ts
Normal file
8
src/api/commodity.ts
Normal file
@ -0,0 +1,8 @@
|
||||
// 商品维度接口
|
||||
const shop = {
|
||||
searchCommodityList: ['/index/page/list'], // 获取商品列表接口
|
||||
getCommodityDetail: ['/index/product/detail'], // 获取商品详情
|
||||
getReviewList: ['/comment/list'], // 获取评价接口
|
||||
}
|
||||
|
||||
export default shop
|
||||
4
src/api/common.ts
Normal file
4
src/api/common.ts
Normal file
@ -0,0 +1,4 @@
|
||||
// 公共接口
|
||||
const common = {}
|
||||
|
||||
export default common
|
||||
22
src/api/index.ts
Normal file
22
src/api/index.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import axios from '@/utils/axios'
|
||||
import user from './user'
|
||||
import shop from './commodity'
|
||||
import common from './common'
|
||||
|
||||
const totalApiConfig = { user, shop, common }
|
||||
|
||||
const handleAddArrayPrototype = (obj: Recordable<[string, Recordable<any>]>) => {
|
||||
Object.values(obj).forEach((arr) => {
|
||||
const [url, other = {}] = arr
|
||||
Object.setPrototypeOf(arr, {
|
||||
post: <T>(data = {}) => axios.post<T>({ url, data, ...other }),
|
||||
get: <T>(data = {}) => axios.get<T>({ url, params: data, ...other }),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
Object.values(totalApiConfig).forEach((apiItem) => {
|
||||
handleAddArrayPrototype(apiItem as any)
|
||||
})
|
||||
|
||||
export default totalApiConfig
|
||||
7
src/api/user.ts
Normal file
7
src/api/user.ts
Normal file
@ -0,0 +1,7 @@
|
||||
// 用户维度接口
|
||||
const user = {
|
||||
getAddressList: ['/buyer/address/list'],
|
||||
updateAddress: ['/buyer/address/insertOrUpdate'],
|
||||
}
|
||||
|
||||
export default user
|
||||
1
src/auto-import.d.ts
vendored
1
src/auto-import.d.ts
vendored
@ -49,6 +49,7 @@ declare global {
|
||||
const shallowReactive: typeof import('vue')['shallowReactive']
|
||||
const shallowReadonly: typeof import('vue')['shallowReadonly']
|
||||
const shallowRef: typeof import('vue')['shallowRef']
|
||||
const showToast: typeof import('vant/es')['showToast']
|
||||
const toRaw: typeof import('vue')['toRaw']
|
||||
const toRef: typeof import('vue')['toRef']
|
||||
const toRefs: typeof import('vue')['toRefs']
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import 'virtual:uno.css'
|
||||
import 'normalize.css'
|
||||
import "@/assets/index.scss"
|
||||
import '@/assets/index.scss'
|
||||
import '../types/global.d.ts'
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
||||
import VConsole from 'vconsole'
|
||||
|
||||
import App from './App.vue'
|
||||
@ -11,7 +13,7 @@ import router from './router'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(createPinia())
|
||||
app.use(createPinia().use(piniaPluginPersistedstate))
|
||||
app.use(router)
|
||||
|
||||
app.mount('#app')
|
||||
|
||||
15
src/settings/index.ts
Normal file
15
src/settings/index.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import type { AppConfigType, PrefixType } from '#/config'
|
||||
export * from './log'
|
||||
|
||||
export const appConfig: Record<PrefixType, AppConfigType> = {
|
||||
dcd: {
|
||||
name: '东成贷',
|
||||
prefix: 'dcd',
|
||||
},
|
||||
}
|
||||
|
||||
export const loginPaths = []
|
||||
|
||||
const LOCAL_PATH = 'test'
|
||||
|
||||
export const baseURL = import.meta.env.DEV ? `/${LOCAL_PATH}/api-interface/app` : '/api-interface/app'
|
||||
12
src/settings/log.ts
Normal file
12
src/settings/log.ts
Normal file
@ -0,0 +1,12 @@
|
||||
export const logConfig = {
|
||||
// 前端日志管理过滤域名
|
||||
interceptDomain: ['https://tapp.dongchengdai.com', 'https://app.dongchengdai.com'],
|
||||
// 前端日志过滤请求地址
|
||||
filterHttpUrl: ['bl-log/web', '/user/baseInfo', '/log/behaviorLogDcd'],
|
||||
// 前端日志管理接口
|
||||
bllogAddUrl: 'https://bllog.yijiesudai.com/bl-log/web/log/add',
|
||||
// vconsole过滤域名
|
||||
vConsoleDomain: '',
|
||||
// sdk过滤域名
|
||||
sdkDomain: ''
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useAppStore = defineStore('app', () => {
|
||||
const themeColor = '#01CF24'
|
||||
|
||||
return { themeColor }
|
||||
})
|
||||
11
src/stores/index.ts
Normal file
11
src/stores/index.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import useAppStore from './modules/app'
|
||||
import useNativeStore from './modules/native'
|
||||
import useLoginStore from './modules/login'
|
||||
|
||||
const useStore = () => ({
|
||||
appStore: useAppStore(),
|
||||
nativeStore: useNativeStore(),
|
||||
userStore: useLoginStore(),
|
||||
})
|
||||
|
||||
export default useStore
|
||||
32
src/stores/modules/app.ts
Normal file
32
src/stores/modules/app.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import type { AppConfigType, EnvType, PlatType } from '#/config'
|
||||
|
||||
export default defineStore(
|
||||
'app',
|
||||
() => {
|
||||
const themeColor = '#01CF24'
|
||||
const env = ref('')
|
||||
const platform = ref('')
|
||||
|
||||
const setPlatform = (val: PlatType) => {
|
||||
platform.value = val
|
||||
}
|
||||
|
||||
const setEnv = () => {
|
||||
const envs: Recordable<EnvType> = {
|
||||
app: 'online',
|
||||
tapp: 'test',
|
||||
}
|
||||
const key = location.host.replace(/^([a-z]+).+/g, '$1')
|
||||
env.value = envs[key] || 'dev'
|
||||
}
|
||||
|
||||
return { themeColor, env, setPlatform, setEnv }
|
||||
},
|
||||
{
|
||||
persist: {
|
||||
key: 'appConfig',
|
||||
storage: localStorage,
|
||||
},
|
||||
},
|
||||
)
|
||||
125
src/stores/modules/login.ts
Normal file
125
src/stores/modules/login.ts
Normal file
@ -0,0 +1,125 @@
|
||||
import axios from 'axios'
|
||||
import Log from 'capture-request-log'
|
||||
import { handleData } from 'lz-utils-lib'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
import { logConfig } from '@/settings'
|
||||
import { getPhoneType, log } from '@/utils/common'
|
||||
// import { secretKeyAesKey } from '@/utils/secret-key'
|
||||
// import type { PlatType } from '#/config'
|
||||
|
||||
import useAppStore from './app'
|
||||
|
||||
export default defineStore('login', {
|
||||
state: () => {
|
||||
return {
|
||||
userInfo: {
|
||||
uuid: '',
|
||||
token: '',
|
||||
mobile: '',
|
||||
},
|
||||
environment: '',
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
async saveLoginInfo(loginData: string) {
|
||||
const appStore = useAppStore()
|
||||
try {
|
||||
const data = handleData.tobase64.decodeObj(decodeURIComponent(loginData))
|
||||
console.log('token:', data.userInfo.token)
|
||||
console.log('environment:', data.environment)
|
||||
console.log('appPackage:', data.appInfo.appPackage)
|
||||
console.log('channel:', data.appInfo.channel)
|
||||
appStore.setEnv()
|
||||
this.userInfo = {
|
||||
uuid: data.userInfo.uuid,
|
||||
token: data.userInfo.token,
|
||||
mobile: '',
|
||||
}
|
||||
/* let platform = ''
|
||||
if (data.appInfo) {
|
||||
platform = data.appInfo.appPackage.replace(/_\w+/, '')
|
||||
appStore.setPlatform(platform as PlatType)
|
||||
}
|
||||
if (data.environment) {
|
||||
this.environment = data.environment
|
||||
} */
|
||||
return Promise.resolve()
|
||||
} catch (e) {
|
||||
console.error('传入returnData有误')
|
||||
}
|
||||
},
|
||||
clearLogin() {
|
||||
this.$reset()
|
||||
//testzc VueCookie.delete('loginData')
|
||||
},
|
||||
// 清除path(直接用)
|
||||
clearPathSearch(keys: string[]) {
|
||||
let path = location.search.slice(1)
|
||||
let str = keys.reduce((data, cur) => {
|
||||
data += cur + '|'
|
||||
return data
|
||||
}, '')
|
||||
str = str.slice(0, -1)
|
||||
path = path.replace(new RegExp(`((${str})=[^&]+)`, 'g'), () => '')
|
||||
path = path.replace(/^[&]+/, '')
|
||||
path = path.replace(/([&]+)/g, '&')
|
||||
path = path ? `?${path}` : path
|
||||
const time = setTimeout(() => {
|
||||
clearTimeout(time)
|
||||
window.history.replaceState(null, '', `${location.pathname}${path}`)
|
||||
}, 1000)
|
||||
},
|
||||
// 上传token和git log(可用:微调)
|
||||
async handleUploadToken(token: string, mobile: string) {
|
||||
const divGitInfo = (document.querySelector('#div-git-info') as HTMLElement)?.dataset.gitInfo
|
||||
const oldData = { commitId: '' }
|
||||
if (divGitInfo) {
|
||||
divGitInfo.replace(/([^&=]+)=([^&]+)/g, (_, k, v) => (oldData[k as keyof typeof oldData] = v))
|
||||
}
|
||||
/* apiCommon.uploadToken.post({
|
||||
mobile,
|
||||
msg: JSON.stringify([`<东成贷> 联合登录url参数:${oldData.commitId}`, `token=${token}`]),
|
||||
deviceType: getPhoneType(),
|
||||
systemCode: 'dongchengdai',
|
||||
systemCodeName: '东成贷',
|
||||
projectCode: 'DCD_H5',
|
||||
projectCodeName: '东成贷H5',
|
||||
title: 'consoleLog',
|
||||
type: 'console',
|
||||
url: location.origin + location.pathname,
|
||||
}) */
|
||||
},
|
||||
// 前端日志上报(可用,微调)
|
||||
async handleCreateLog(app: any) {
|
||||
const appStore = useAppStore()
|
||||
let mobile: string
|
||||
new Log({
|
||||
window,
|
||||
Vue: appStore.env !== 'dev' ? app : undefined,
|
||||
filterHttpUrl: logConfig.filterHttpUrl,
|
||||
interceptDomain: logConfig.interceptDomain,
|
||||
sendError: async (obj: any = {}) => {
|
||||
const msgOb = JSON.parse(obj.msg || '{}')
|
||||
const url = msgOb.url ? msgOb.url.replace(/.+app-web/g, '') : ''
|
||||
Object.assign(obj, {
|
||||
// mobile,
|
||||
// secretKeyAesKey: secretKeyAesKey[url],
|
||||
systemCode: 'dongchengdai',
|
||||
systemCodeName: '东成贷',
|
||||
deviceType: getPhoneType(),
|
||||
projectCode: 'DCD_H5',
|
||||
projectCodeName: '东成贷H5',
|
||||
})
|
||||
// delete secretKeyAesKey[url]
|
||||
axios.post(logConfig.bllogAddUrl, obj)
|
||||
},
|
||||
})
|
||||
window.log = log
|
||||
},
|
||||
},
|
||||
persist: {
|
||||
key: 'login',
|
||||
storage: localStorage,
|
||||
},
|
||||
})
|
||||
46
src/stores/modules/native.ts
Normal file
46
src/stores/modules/native.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import type { DCAndNativeNameType, DCNativeNameType } from 'lz-utils-lib'
|
||||
import { DC, handleData } from 'lz-utils-lib'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
import api from '@/api'
|
||||
import router from '@/router'
|
||||
import { handleGoThirdPage } from '@/utils/common'
|
||||
import { openMinProgram } from '@/utils/wx-minprogram'
|
||||
|
||||
export default defineStore('native', {
|
||||
state: (): { appInfo: any } => {
|
||||
return {
|
||||
appInfo: Object.create(null) as any,
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
version(state) {
|
||||
return state.appInfo.version ? state.appInfo.version : ''
|
||||
},
|
||||
appPackage() {
|
||||
return 'xcx_dc'
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
jumpRouter(type) {
|
||||
if (this.appPackage === 'xcx_dc') {
|
||||
switch (type) {
|
||||
case 'home':
|
||||
openMinProgram('index')
|
||||
break
|
||||
case 'logout':
|
||||
openMinProgram('index')
|
||||
break
|
||||
case 'login':
|
||||
openMinProgram('login')
|
||||
break
|
||||
default:
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
persist: {
|
||||
key: 'nativeConfig',
|
||||
storage: localStorage,
|
||||
},
|
||||
})
|
||||
97
src/utils/axios.ts
Normal file
97
src/utils/axios.ts
Normal file
@ -0,0 +1,97 @@
|
||||
import 'vant/es/toast/style'
|
||||
|
||||
import axios, { AxiosError, type AxiosInstance, type AxiosRequestConfig, type AxiosResponse, type InternalAxiosRequestConfig } from 'axios'
|
||||
import { showToast } from 'vant'
|
||||
|
||||
import { baseURL } from '@/settings'
|
||||
import useStore from '@/stores'
|
||||
|
||||
import { loadingData } from './page'
|
||||
|
||||
// 创建axios实例
|
||||
const service: AxiosInstance = axios.create({
|
||||
baseURL, // api 的 base_url
|
||||
timeout: 150000, // 请求超时时间
|
||||
})
|
||||
|
||||
const noEncryptWhiteList = ['/common/protocol/content', '/portal/common/protocol/content', '/bl-log/web/log/add']
|
||||
const noConsoleWhiteList = ['bl-log/web/log/add']
|
||||
|
||||
// request拦截器
|
||||
service.interceptors.request.use(
|
||||
async (config: InternalAxiosRequestConfig) => {
|
||||
const { userStore, appStore } = useStore()
|
||||
if (!/^http/.test(config.url!)) {
|
||||
config.headers!.token = userStore.userInfo.token || ''
|
||||
}
|
||||
if (!(config.headers!['Content-Type'] as string)?.endsWith('multipart/form-data')) {
|
||||
if (config.method === 'get') {
|
||||
if (config.params?.isLoading) {
|
||||
config.isLoading = true
|
||||
delete config.params.isLoading
|
||||
loadingData.type === 'submit' && (loadingData.show = true)
|
||||
}
|
||||
} else {
|
||||
config.data.buyerId = 2 // testzc
|
||||
config.data.buyerWeixin = '我是微信号' // testzc
|
||||
config.isLoading = config.data.isLoading
|
||||
delete config.data.isLoading
|
||||
config.isLoading && loadingData.type === 'submit' && (loadingData.show = true)
|
||||
}
|
||||
}
|
||||
return config
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
// Do something with request error
|
||||
console.log(error) // for debug
|
||||
Promise.reject(error)
|
||||
},
|
||||
)
|
||||
|
||||
// response 拦截器
|
||||
service.interceptors.response.use(
|
||||
(response: AxiosResponse<any>) => {
|
||||
const { nativeStore } = useStore()
|
||||
if (response.config.responseType === 'blob' || response.config.third) {
|
||||
// 如果是文件流,直接过
|
||||
return response
|
||||
}
|
||||
if (!['200'].includes(response.data.code)) {
|
||||
showToast({ message: response.data.desc, duration: 3000 })
|
||||
if (response.data.code === 401) {
|
||||
setTimeout(() => {
|
||||
nativeStore.jumpRouter('login')
|
||||
}, 1000)
|
||||
return
|
||||
}
|
||||
if (response.config.isLoading && loadingData.type === 'submit') {
|
||||
loadingData.show = false
|
||||
}
|
||||
return Promise.reject(response.data)
|
||||
}
|
||||
if (response.data.code === '200' || response.config.toast === false) {
|
||||
return response.data
|
||||
}
|
||||
if (response.config.isLoading && loadingData.type === 'submit') {
|
||||
loadingData.show = false
|
||||
}
|
||||
setTimeout(() => {
|
||||
showToast({ message: response.data.msg, duration: 3000 })
|
||||
}, 100)
|
||||
return Promise.reject(response.data)
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
console.log(`err${error}`) // for debug
|
||||
showToast(error.message)
|
||||
loadingData.type === 'submit' && (loadingData.show = false)
|
||||
return Promise.reject(error)
|
||||
},
|
||||
)
|
||||
|
||||
export const request = <T, K, F>(config: AxiosRequestConfig<T>): Promise<F extends { third: boolean } ? K : { code: string | number; msg: string; data: K }> => service.request(config)
|
||||
export default {
|
||||
get: <K = unknown, F = string, T = object>(config: AxiosRequestConfig<T>) => request<T, K, F>({ ...config, method: 'get' }),
|
||||
post: <K = unknown, F = string, T = object>(config: AxiosRequestConfig<T>) => request<T, K, F>({ ...config, method: 'post' }),
|
||||
delete: <K = unknown, F = string, T = object>(config: AxiosRequestConfig<T>) => request<T, K, F>({ ...config, method: 'delete' }),
|
||||
put: <K = unknown, F = string, T = object>(config: AxiosRequestConfig<T>) => request<T, K, F>({ ...config, method: 'put' }),
|
||||
}
|
||||
99
src/utils/common.ts
Normal file
99
src/utils/common.ts
Normal file
@ -0,0 +1,99 @@
|
||||
import { logConfig } from '@/settings'
|
||||
import { map } from 'lodash-es'
|
||||
import MobileDetect from 'mobile-detect'
|
||||
import { getData } from 'lz-utils-lib'
|
||||
import useAppStore from '@/stores/modules/app'
|
||||
import router from '@/router'
|
||||
|
||||
// 获取手机具体型号
|
||||
export const getPhoneType = () => {
|
||||
try {
|
||||
const deviceType = navigator.userAgent // 获取userAgent信息
|
||||
const md = new MobileDetect(deviceType) // 初始化mobile-detect
|
||||
let os = md.os() // 获取系统
|
||||
let model = '' as any
|
||||
if (os === 'iOS') {
|
||||
// ios系统的处理
|
||||
os = md.os() + ' ' + md.version('iPhone')
|
||||
model = md.mobile()
|
||||
} else if (os === 'AndroidOS') {
|
||||
// Android系统的处理
|
||||
os = md.os().replace('OS', '') + ' ' + md.version('Android')
|
||||
const sss = deviceType.split(';')
|
||||
const i = sss.findIndex((val) => val.includes('Build/'))
|
||||
if (i) {
|
||||
model = sss[i].substring(0, sss[i].indexOf('Build/')).trim()
|
||||
}
|
||||
}
|
||||
return os + ' ' + model
|
||||
} catch (e) {
|
||||
return '未知型号'
|
||||
}
|
||||
}
|
||||
|
||||
// 判断是线上还是开发环境打印日志
|
||||
export const log = (...arg: any) => {
|
||||
try {
|
||||
if (logConfig.interceptDomain.includes(location.origin)) {
|
||||
// 线上环境
|
||||
window.captureLog(...arg)
|
||||
} else {
|
||||
console.log(...arg)
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(...arg)
|
||||
}
|
||||
}
|
||||
|
||||
// url参数变成参数字符串
|
||||
export const getQuerystring = (data: Record<string, any>) => {
|
||||
return map(data, (val: any, key: string) => `${encodeURIComponent(key)}=${encodeURIComponent(typeof val === 'object' ? JSON.stringify(val) : val)}`).join('&')
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转url
|
||||
* @param url: https://www.baidu.com; https://lth5.yijiesudai.com/unicom/a; /borrow/money;
|
||||
*/
|
||||
export const handleGoThirdPage = (url: string, name?: string, urlParams?: Record<string, any>) => {
|
||||
const newParams = { ...getData.getUrlParams(url), ...(urlParams || {}) }
|
||||
const query = getQuerystring(newParams)
|
||||
const newUrl = url.split('?')[0]
|
||||
if (newUrl.includes(`/${import.meta.env.VITE_APP_CODE}/`)) {
|
||||
const regex = new RegExp(`.*(/${import.meta.env.VITE_APP_CODE})([^?]+).*`)
|
||||
const path = newUrl.replace(regex, '$2')
|
||||
router.push({ path, query: newParams })
|
||||
} else if (newUrl.endsWith('.pdf')) {
|
||||
handleGoPdfPage(newUrl, name, url.split('?')[1])
|
||||
} else {
|
||||
window.location.href = newUrl + '?' + query
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pdf跳转
|
||||
* @param url pdf链接
|
||||
*/
|
||||
export const handleGoPdfPage = (url: string, name: string, search: string = '') => {
|
||||
const appStore = useAppStore()
|
||||
/*
|
||||
// 小程序:等小程序上线后再使用
|
||||
if (appStore.platform === 'xcx') {
|
||||
// window.useLoading({ type: 'submit', show: true })
|
||||
// window.wx.miniProgram.postMessage({ data: { pdfUrl: url + '?' + search } })
|
||||
window.wx.miniProgram.navigateTo({ url: `/pages/pdf/index?url=${encodeURIComponent(url)}` })
|
||||
} else */
|
||||
if (getPhoneType().toLocaleLowerCase().startsWith('ios')) {
|
||||
window.location.href = url + '?' + search
|
||||
} else {
|
||||
sessionStorage.setItem('picUrlName', name)
|
||||
const prefixConfig = {
|
||||
'ltf.yijiesudai': '/yjsd',
|
||||
'dcd-pri.oss-cn-hangzhou': '/dcd-pri',
|
||||
'dcd-pri.dongchengdai.com': '/dcd-pri',
|
||||
'ltyijie.oss-cn-hangzhou': '/yjsd',
|
||||
}
|
||||
const prefix = prefixConfig[Object.keys(prefixConfig).find((key) => url.includes(key))]
|
||||
const newUrl = url.replace(/(https?:\/\/)?[^/?]+/, window.location.origin + (appStore.env === 'dev' ? '/test' : '') + prefix)
|
||||
window.location.href = window.location.origin + '/dcmb/pdfjs/web/viewer.html?file=' + encodeURIComponent(newUrl + '?' + search)
|
||||
}
|
||||
}
|
||||
63
src/utils/page.ts
Normal file
63
src/utils/page.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import { closeToast, showLoadingToast, type ToastWrapperInstance } from 'vant'
|
||||
|
||||
interface ApiType {
|
||||
api: <T, K>(data?: T) => Promise<{ code: string; msg: string; data: K }>
|
||||
params?: object
|
||||
}
|
||||
|
||||
export const useCreatePageData = (apis: ApiType[]) => {
|
||||
return Promise.all(apis.map((item: ApiType) => item.api(item.params)))
|
||||
}
|
||||
|
||||
// 提交loading
|
||||
export const useSubmitLoading = (message = '提交中...') => {
|
||||
showLoadingToast({ duration: 0, overlay: true, forbidClick: true, message })
|
||||
}
|
||||
useSubmitLoading.close = () => {
|
||||
closeToast()
|
||||
}
|
||||
|
||||
// 将日期转为数组
|
||||
export const handleDateToArray = (date: Date) => {
|
||||
return [date.getFullYear().toString(), (date.getMonth() + 1).toString().padStart(2, '0'), date.getDate().toString().padStart(2, '0')]
|
||||
}
|
||||
|
||||
window.isBgWhite = ref(true)
|
||||
export const isBgWhite = window.isBgWhite
|
||||
|
||||
// 获取loading
|
||||
export const loadingData = reactive({ if: null as null | boolean, type: 'enter', show: false })
|
||||
window.useLoading = (ob: { type: 'enter' | 'submit'; show: boolean } = { type: 'enter', show: true }) => {
|
||||
loadingData.if ??= true
|
||||
return Object.assign(loadingData, ob)
|
||||
}
|
||||
|
||||
// 提示弹窗
|
||||
export const tipDialogData = reactive({
|
||||
if: null as null | boolean,
|
||||
show: false,
|
||||
showCloseIcon: false,
|
||||
showCancelBtn: false,
|
||||
cancelBtnName: '',
|
||||
title: '',
|
||||
html: '',
|
||||
btnName: '',
|
||||
callback: (() => {}) as (result: boolean) => void | Promise<any>,
|
||||
})
|
||||
window.useTipDialog = (
|
||||
callback: (result: boolean) => void | Promise<any>,
|
||||
config: {
|
||||
title: string
|
||||
html: string
|
||||
btnName: string
|
||||
showCloseIcon?: boolean
|
||||
showCancelBtn?: boolean
|
||||
cancelBtnName?: string
|
||||
},
|
||||
) => {
|
||||
tipDialogData.showCancelBtn = false
|
||||
Object.assign(tipDialogData, config)
|
||||
tipDialogData.if ??= true
|
||||
tipDialogData.show = true
|
||||
tipDialogData.callback = callback
|
||||
}
|
||||
36
src/utils/wx-minprogram.ts
Normal file
36
src/utils/wx-minprogram.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import api from '@/api'
|
||||
/**
|
||||
* 小程序内H5打开小程序页面
|
||||
* @param {string} type 小程序页面:index首页,mine我的
|
||||
*/
|
||||
export const openMinProgram = (type = 'index') => {
|
||||
if (['index', 'mine'].includes(type)) {
|
||||
window.wx.miniProgram.switchTab({ url: `/pages/${type}/index` })
|
||||
} else {
|
||||
window.wx.miniProgram.redirectTo({ url: `/pages/${type}/index` })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 小程序外H5打开小程序页面
|
||||
* @param {string} type 小程序页面:index首页,mine我的,webview页面
|
||||
* @param {string} h5Url 小程序中H5页面地址(type=webview时存在此值)
|
||||
*/
|
||||
export const h5OpenMinProgram = (type = 'index', h5Url = '') => {
|
||||
const params = {}
|
||||
if (type) {
|
||||
Object.assign(params, { backUrl: `/pages/${type}/index` })
|
||||
}
|
||||
if (h5Url && !h5Url.startsWith('https')) {
|
||||
Object.assign(params, {
|
||||
query: `url=${encodeURIComponent(`${window.location.origin}${h5Url}`)}`
|
||||
})
|
||||
}
|
||||
api.getSchemeUrl.post(params).then(res => {
|
||||
if (res.data) {
|
||||
window.location.href = res.data as any
|
||||
} else {
|
||||
showToast('跳转链接有误,请联系客服!')
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -4,17 +4,15 @@
|
||||
<div class="pt-3 flex-1 overflow-y-scroll">
|
||||
<van-form @submit="onSubmit">
|
||||
<van-cell-group inset>
|
||||
<van-field v-model="formData.name" label="名字" placeholder="请输入收货人名称" :rules="[{ required: true, message: '请输入收货人名称' }]" />
|
||||
<van-field v-model="formData.mobile" label="手机号" placeholder="请输入收货人手机号" :rules="[{ required: true, message: '请输入收货人手机号' }]" />
|
||||
<van-field v-model="formData.area" is-link readonly name="area" label="地区选择" placeholder="点击选择省市区" @click="areaData.show = true" />
|
||||
<van-field v-model="formData.addressDetail" rows="3" autosize label="详细地址" type="textarea" placeholder="请输入详细地址" :rules="[{ required: true, message: '请输入详细地址' }]" />
|
||||
<van-field name="checkboxGroup" label="" class="py-1!">
|
||||
<template #input>
|
||||
<van-checkbox-group v-model="formData.default" direction="horizontal">
|
||||
<van-checkbox :name="1" shape="square" class="text-xs">默认地址</van-checkbox>
|
||||
</van-checkbox-group>
|
||||
<van-field v-model="formData.buyerName" label="名字" placeholder="请输入收货人名称" :rules="[{ required: true, message: '请输入收货人名称' }]" />
|
||||
<van-field v-model="formData.buyerPhone" label="手机号" placeholder="请输入收货人手机号" :rules="[{ required: true, message: '请输入收货人手机号' }]" />
|
||||
<van-field v-model="formData.areaName" is-link readonly name="areaName" label="地区选择" placeholder="点击选择省市区" @click="areaData.show = true" />
|
||||
<van-field v-model="formData.detail" rows="3" autosize label="详细地址" type="textarea" placeholder="请输入详细地址" :rules="[{ required: true, message: '请输入详细地址' }]" />
|
||||
<van-cell title="设为默认地址">
|
||||
<template #right-icon>
|
||||
<van-switch v-model="formData.status" size=".2rem" active-value="default" inactive-value="common" :active-color="appStore.themeColor" />
|
||||
</template>
|
||||
</van-field>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
<div class="bg-white w-full h-12 p-2 box-border">
|
||||
<button class="block w-full h-full border-none theme-bg-color color-white rounded-[.04rem]" native-type="submit">确定</button>
|
||||
@ -23,26 +21,62 @@
|
||||
</div>
|
||||
</van-popup>
|
||||
<van-popup v-model:show="areaData.show" destroy-on-close position="bottom">
|
||||
<van-area :area-list="areaList" :model-value="areaData.pickerValue" @confirm="onSelectArea" @cancel="areaData.show = false" />
|
||||
<van-area :area-list="areaList" :model-value="areaData.areaCode" @confirm="onSelectArea" @cancel="areaData.show = false" />
|
||||
</van-popup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import api from '@/api'
|
||||
import useAppStore from '@/stores/modules/app'
|
||||
import { areaList } from '@vant/area-data'
|
||||
const model = defineModel<boolean>()
|
||||
defineProps<{ isEdit: boolean }>()
|
||||
const formData = reactive({ name: '', mobile: '', area: '', addressDetail: '', province: '', city: '', region: '', default: [] })
|
||||
const formData = ref({ id: NaN, buyerName: '', buyerPhone: '', areaName: '', district: '', detail: '', province: '', city: '', status: 'common' })
|
||||
|
||||
const areaData = reactive({ show: false, pickerValue: '' })
|
||||
const areaData = reactive({ show: false, areaCode: '' })
|
||||
const appStore = useAppStore()
|
||||
|
||||
const editData = defineModel<Recordable<any>>('editData')
|
||||
watch(
|
||||
model,
|
||||
(val) => {
|
||||
console.warn('----- my data is 111: ', editData.value.id)
|
||||
if (val && editData.value.id) {
|
||||
const data = editData.value
|
||||
formData.value = {
|
||||
id: data.id,
|
||||
buyerName: data.buyerName,
|
||||
buyerPhone: data.buyerPhone,
|
||||
areaName: data.province + ' ' + data.city + ' ' + data.district,
|
||||
district: data.district,
|
||||
detail: data.detail,
|
||||
province: data.province,
|
||||
city: data.city,
|
||||
status: data.status,
|
||||
}
|
||||
areaData.areaCode = data.areaCode
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
const onSelectArea = ({ selectedValues, selectedOptions }: { selectedOptions: any; selectedValues: any }) => {
|
||||
areaData.pickerValue = selectedValues.length ? selectedValues[selectedValues.length - 1] : ''
|
||||
areaData.areaCode = selectedValues.length ? selectedValues[selectedValues.length - 1] : ''
|
||||
areaData.show = false
|
||||
formData.area = selectedOptions.map((item: any) => item.text).join(' ')
|
||||
formData.value.province = selectedOptions[0].text
|
||||
formData.value.city = selectedOptions[1].text
|
||||
formData.value.district = selectedOptions[2].text
|
||||
formData.value.areaName = selectedOptions.map((item: any) => item.text).join(' ')
|
||||
}
|
||||
|
||||
const emits = defineEmits(['confirm'])
|
||||
const onSubmit = () => {
|
||||
model.value = false
|
||||
const params = { ...formData.value, contry: '中国', areaCode: areaData.areaCode }
|
||||
delete params.areaName
|
||||
api.user.updateAddress.post(params).then(() => {
|
||||
model.value = false
|
||||
emits('confirm')
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@ -1,49 +1,86 @@
|
||||
<template>
|
||||
<van-popup id="address-list" class="flex flex-col" v-model:show="model" position="bottom" :style="{ height: '70%' }" closeable round>
|
||||
<h3 class="text-center pt-3 text-base">收货地址管理</h3>
|
||||
<h4 class="flex justify-between items-center px-4 py-4 text-sm font-bold">
|
||||
<span>常用地址</span>
|
||||
<p>
|
||||
<b @click="isManage = !isManage" class="font-bold">{{ !isManage ? '管理' : '退出管理' }}</b
|
||||
><b class="ml-3 theme-color font-bold" @click="showAddAddressFormDialog = true">新增地址</b>
|
||||
</p>
|
||||
</h4>
|
||||
<div class="flex-1 overflow-y-scroll">
|
||||
<van-cell-group class="address-list__container">
|
||||
<van-cell :class="['address-list__item']" v-for="item in addressList" :key="item.id">
|
||||
<template #title>
|
||||
<div class="item__info">
|
||||
<h6 class="text-[.1rem]">浙江省 杭州市 萧山区 宁围街道</h6>
|
||||
<p class="item__info__detail">湘湖路与事假达到交叉口 新希望宁问哦小区2幢2单元202</p>
|
||||
<p class="item__info__subtitle">张丹 15100001111</p>
|
||||
</div>
|
||||
</template>
|
||||
<template #value>
|
||||
<div class="item__editor text-base">
|
||||
<van-icon v-if="!isManage" name="edit" color="#999" @click="onEditAddress" />
|
||||
<van-icon v-else name="delete-o" />
|
||||
</div>
|
||||
</template>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
<div class="bg-white w-full h-12 p-2 box-border">
|
||||
<button class="block w-full h-full border-none bg-red color-white rounded-[.04rem]">确定</button>
|
||||
<div>
|
||||
<van-popup id="address-list" class="flex flex-col" v-model:show="model" position="bottom" :style="{ height: '70%' }" closeable round>
|
||||
<h3 class="text-center pt-3 text-base">收货地址管理</h3>
|
||||
<h4 class="flex justify-between items-center px-4 py-4 text-sm">
|
||||
<span>常用地址</span>
|
||||
<p>
|
||||
<b @click="isManage = !isManage">{{ !isManage ? '管理' : '退出管理' }}</b
|
||||
><b class="ml-3 theme-color" @click="onAddOrEditAddress('add')">新增地址</b>
|
||||
</p>
|
||||
</h4>
|
||||
<div class="flex-1 overflow-y-scroll">
|
||||
<van-cell-group class="address-list__container">
|
||||
<van-cell :class="['address-list__item']" v-for="item in addressList" :key="item.id">
|
||||
<template #title>
|
||||
<div class="item__info">
|
||||
<h6 class="text-[.1rem]">{{ item.area }}</h6>
|
||||
<p class="item__info__detail">{{ item.detail }}</p>
|
||||
<p class="item__info__subtitle text-[.13rem]">{{ item.name }} {{ item.tel }}</p>
|
||||
</div>
|
||||
</template>
|
||||
<template #value>
|
||||
<div class="item__editor text-base">
|
||||
<van-icon v-if="!isManage" name="edit" color="#999" @click="onAddOrEditAddress('edit', item.originData)" />
|
||||
<van-icon v-else name="delete-o" />
|
||||
</div>
|
||||
</template>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
<div class="bg-white w-full h-12 p-2 box-border">
|
||||
<button class="block w-full h-full border-none bg-red color-white rounded-[.04rem]">确定</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</van-popup>
|
||||
</van-popup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import api from '@/api'
|
||||
|
||||
const model = defineModel<boolean>()
|
||||
const addressFormType = defineModel<'add' | 'edit'>('addressFormType')
|
||||
const showAddAddressFormDialog = defineModel<boolean>('showAddAddressFormDialog')
|
||||
const addressList = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 5 }]
|
||||
const isUpDateAddressList = defineModel<boolean>('isUpDateAddressList')
|
||||
const addressList = ref([])
|
||||
|
||||
const emits = defineEmits(['edit', 'confirm'])
|
||||
|
||||
watch(model, (val) => {
|
||||
val && handleGetAddressData()
|
||||
})
|
||||
|
||||
// 是否需要更新地址列表
|
||||
watch(isUpDateAddressList, (val) => {
|
||||
val && handleGetAddressData()
|
||||
isUpDateAddressList.value = false
|
||||
})
|
||||
|
||||
const isManage = ref(false)
|
||||
|
||||
const onEditAddress = () => {
|
||||
const onAddOrEditAddress = (type: 'edit' | 'add', data?: any) => {
|
||||
showAddAddressFormDialog.value = true
|
||||
addressFormType.value = 'edit'
|
||||
if (type === 'edit') {
|
||||
emits('edit', data)
|
||||
}
|
||||
addressFormType.value = type
|
||||
}
|
||||
|
||||
const handleGetAddressData = () => {
|
||||
api.user.getAddressList.post<any>().then((res) => {
|
||||
addressList.value = res.data.rows.map((item) => {
|
||||
const originData = { ...item }
|
||||
return {
|
||||
id: item.id,
|
||||
name: item.buyerName,
|
||||
tel: item.buyerPhone,
|
||||
area: item.contry + item.province + item.city + item.district,
|
||||
detail: item.detail,
|
||||
isDefault: item.status === 'default',
|
||||
originData,
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -62,7 +99,6 @@ const onEditAddress = () => {
|
||||
width: 0;
|
||||
}
|
||||
.item__info__detail {
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
@ -50,14 +50,22 @@
|
||||
<button class="theme-bg-color color-white" @click="onPayNowBtn">立即购买</button>
|
||||
</div>
|
||||
</footer>
|
||||
<shopping-cart v-model="showShopCart" v-model:showAddressList="showAddressList"></shopping-cart>
|
||||
<shopping-cart v-model="showShopCart" v-model:showAddressList="showAddressList" v-model:curAddressData="curAddressData"></shopping-cart>
|
||||
<all-review v-model="showAllReview"></all-review>
|
||||
<address-list v-model="showAddressList" v-model:showAddAddressFormDialog="showAddAddressFormDialog" v-model:addressFormType="addressFormType"></address-list>
|
||||
<address-form v-model="showAddAddressFormDialog" :isEdit="addressFormType === 'edit'"></address-form>
|
||||
<address-list
|
||||
v-model="showAddressList"
|
||||
v-model:isUpDateAddressList="isUpDateAddressList"
|
||||
v-model:showAddAddressFormDialog="showAddAddressFormDialog"
|
||||
v-model:addressFormType="addressFormType"
|
||||
@edit="(val) => (editAddressData = val)"
|
||||
@confirm="(val) => (curAddressData = val)"
|
||||
></address-list>
|
||||
<address-form v-model="showAddAddressFormDialog" v-model:editData="editAddressData" :isEdit="addressFormType === 'edit'" @confirm="isUpDateAddressList = true"></address-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import api from '@/api'
|
||||
import allReview from './components/all-review.vue'
|
||||
import reviewSingle from './components/review-single.vue'
|
||||
import infoTable from './components/info-table.vue'
|
||||
@ -71,6 +79,15 @@ const showAllReview = ref(false) // 展示全部评价
|
||||
const showAddressList = ref(false) // 展示收货地址列表
|
||||
const showAddAddressFormDialog = ref(false) // 展示收货地址form
|
||||
const addressFormType = ref<'add' | 'edit'>('add')
|
||||
const isUpDateAddressList = ref(false) // 是否需要更新收货地址列表
|
||||
const editAddressData = ref({}) // 编辑的收货地址
|
||||
const curAddressData = ref({}) // 当前选中的收货地址
|
||||
|
||||
const route = useRoute()
|
||||
const init = () => {
|
||||
handleGetReviews(+route.query.id)
|
||||
}
|
||||
|
||||
// 查看全部评价
|
||||
const onShowAllReview = () => {
|
||||
showAllReview.value = true
|
||||
@ -79,6 +96,13 @@ const onShowAllReview = () => {
|
||||
const onPayNowBtn = () => {
|
||||
showShopCart.value = true
|
||||
}
|
||||
|
||||
const handleGetReviews = (productId: number) => {
|
||||
api.shop.getReviewList.post({ productId }).then((res) => {
|
||||
console.log(res)
|
||||
})
|
||||
}
|
||||
init()
|
||||
</script>
|
||||
|
||||
<style scope lang="scss">
|
||||
|
||||
@ -35,7 +35,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import api from '@/api'
|
||||
|
||||
const tabActive = ref('0')
|
||||
|
||||
const reviews = ref([
|
||||
{ id: 1, name: '新疆大柚子水电费水电费水电费手动阀是是否神鼎飞丹砂', rate: 0 },
|
||||
{ id: 2, name: '新疆大柚子水电费水电费水电费手动阀是是否神鼎飞丹砂', rate: 0 },
|
||||
@ -52,6 +55,9 @@ const nums = ref([
|
||||
{ name: '已评价', num: 0 },
|
||||
{ name: '待评价', num: 0 },
|
||||
])
|
||||
|
||||
const init = () => {}
|
||||
|
||||
const router = useRouter()
|
||||
const onGoReview = (id: number) => {
|
||||
router.push({ name: 'review/write', query: { id } })
|
||||
|
||||
@ -1,27 +1,30 @@
|
||||
<template>
|
||||
<div id="search-container" class="h-screen bg-[#f2f2f2] flex flex-col">
|
||||
<header class="search-header flex items-center p-2">
|
||||
<van-search class="flex-1 p-0!" background="#f2f2f2" v-model="searchValue" placeholder="请输入搜索关键词" />
|
||||
<van-search class="flex-1 p-0!" background="#f2f2f2" v-model="searchValue" placeholder="请输入搜索关键词" @search="onSearch(searchValue)" />
|
||||
<van-icon name="shopping-cart-o" size=".2rem" class="ml-2" />
|
||||
</header>
|
||||
<van-tabs v-model:active="searchSort" sticky :color="appStore.themeColor">
|
||||
<van-tabs v-model:active="searchSort" sticky :color="appStore.themeColor" @change="onChangeSort">
|
||||
<van-tab title="综合" name="0"></van-tab>
|
||||
<van-tab title="销量" name="1"></van-tab>
|
||||
<van-tab title="价格" name="2"></van-tab>
|
||||
</van-tabs>
|
||||
<ul class="search-condition bg-white pt-2 flex-1 overflow-y-scroll">
|
||||
<li v-for="item in searchList" :key="item.id" class="flex item-center p-2 relative" @click="onGoDetail(item.id)">
|
||||
<img src="" alt="" class="w-24 h-24" />
|
||||
<img :src="item.mainImageUrl" alt="" class="w-24 h-24" />
|
||||
<div class="ml-2">
|
||||
<h4 class="text-[.13rem]">{{ item.name }}</h4>
|
||||
<p class="text-[#888] mt-1">新鲜优质 | 酸甜多汁 | 足斤足两</p>
|
||||
<h4 class="text-[.13rem]">{{ item.title }}</h4>
|
||||
<!-- <p class="text-[#888] mt-1">新鲜优质 | 酸甜多汁 | 足斤足两</p>
|
||||
<p class="mt-1">
|
||||
<van-tag plain type="primary" color="#ec4d3c">标签1</van-tag>
|
||||
<van-tag plain type="primary" color="#ec4d3c">标签2</van-tag>
|
||||
<van-tag plain type="primary" color="#ec4d3c">标签3</van-tag>
|
||||
</p>
|
||||
</p> -->
|
||||
<p class="mt-3">
|
||||
<span class="text-[#e71e1e] font-bold">¥<b class="font-bold text-base ml-0.5">0</b>.01</span>
|
||||
<span class="text-[#e71e1e] font-bold"
|
||||
>¥<b class="font-bold text-base ml-0.5">{{ item.price.split('.')[0] }}</b
|
||||
>{{ '.' + item.price.split('.')[0] }}</span
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
<van-icon name="add" :color="appStore.themeColor" size=".2rem" class="absolute! right-3 bottom-6 h-5" />
|
||||
@ -31,20 +34,13 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useAppStore } from '@/stores/app.ts'
|
||||
const searchValue = ref('')
|
||||
import api from '@/api'
|
||||
import useAppStore from '@/stores/modules/app'
|
||||
const route = useRoute()
|
||||
const searchValue = ref(route.query.initValue as string)
|
||||
const searchSort = ref('0')
|
||||
const searchList = ref([
|
||||
{ id: 1, name: '新疆大柚子水电费水电费水电费手动阀是是否神鼎飞丹砂' },
|
||||
{ id: 2, name: '新疆大柚子水电费水电费水电费手动阀是是否神鼎飞丹砂' },
|
||||
{ id: 3, name: '新疆大柚子水电费水电费水电费手动阀是是否神鼎飞丹砂' },
|
||||
{ id: 1, name: '新疆大柚子水电费水电费水电费手动阀是是否神鼎飞丹砂' },
|
||||
{ id: 2, name: '新疆大柚子水电费水电费水电费手动阀是是否神鼎飞丹砂' },
|
||||
{ id: 3, name: '新疆大柚子水电费水电费水电费手动阀是是否神鼎飞丹砂' },
|
||||
{ id: 1, name: '新疆大柚子水电费水电费水电费手动阀是是否神鼎飞丹砂' },
|
||||
{ id: 2, name: '新疆大柚子水电费水电费水电费手动阀是是否神鼎飞丹砂' },
|
||||
{ id: 3, name: '新疆大柚子水电费水电费水电费手动阀是是否神鼎飞丹砂' },
|
||||
])
|
||||
|
||||
const searchList = ref([])
|
||||
|
||||
const appStore = useAppStore()
|
||||
|
||||
@ -52,6 +48,24 @@ const router = useRouter()
|
||||
const onGoDetail = (id: number) => {
|
||||
router.push({ name: 'commodity-detail', query: { id } })
|
||||
}
|
||||
|
||||
const onSearch = async (searchValue: string, sort = '0') => {
|
||||
searchSort.value = sort
|
||||
const result = await api.shop.searchCommodityList.post<any>({ productName: searchValue, frontPage: 0, isTest: 0, sort: searchSort.value }) // testzc排序
|
||||
searchList.value = result.data.rows.map((item) => ({
|
||||
id: item.id,
|
||||
title: item.title,
|
||||
mainImageUrl: item.mainImageUrl,
|
||||
originPrice: String(item.showSalePrice),
|
||||
price: String(item.showPromotionPrice),
|
||||
}))
|
||||
}
|
||||
|
||||
const onChangeSort = async (val: string) => {
|
||||
onSearch(searchValue.value, val)
|
||||
}
|
||||
|
||||
onSearch(searchValue.value)
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@ -3,10 +3,12 @@
|
||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
||||
"exclude": ["src/**/__tests__/*"],
|
||||
"compilerOptions": {
|
||||
"strict": false,
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
|
||||
"typeRoots": ["./node_modules/@types", "./types"],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
"@/*": ["./src/*"],
|
||||
"#/*": ["./types/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,19 +1,18 @@
|
||||
{
|
||||
"extends": "@tsconfig/node22/tsconfig.json",
|
||||
"include": [
|
||||
"vite.config.*",
|
||||
"vitest.config.*",
|
||||
"cypress.config.*",
|
||||
"nightwatch.conf.*",
|
||||
"playwright.config.*",
|
||||
"eslint.config.*"
|
||||
],
|
||||
"include": ["vite.config.*", "vitest.config.*", "cypress.config.*", "nightwatch.conf.*", "playwright.config.*", "eslint.config.*", "types/**/*.ts"],
|
||||
"compilerOptions": {
|
||||
"noEmit": true,
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
|
||||
"module": "ESNext",
|
||||
"strict": false,
|
||||
"moduleResolution": "Bundler",
|
||||
"types": ["node"]
|
||||
"types": ["node"],
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@/*": ["src/*"],
|
||||
"#/*": ["types/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
11
types/axios.d.ts
vendored
Normal file
11
types/axios.d.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import { AxiosRequestConfig } from 'axios'
|
||||
// import { Ref } from 'vue'
|
||||
|
||||
declare module 'axios' {
|
||||
export interface AxiosRequestConfig {
|
||||
toast?: boolean
|
||||
third?: boolean
|
||||
isLoading?: boolean
|
||||
}
|
||||
}
|
||||
10
types/config.d.ts
vendored
Normal file
10
types/config.d.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
export type PrefixType = 'dcd'
|
||||
|
||||
export type EnvType = 'online' | 'test' | 'dev'
|
||||
export type PlatType = 'h5' | 'app' | 'xcx'
|
||||
|
||||
export interface AppConfigType {
|
||||
name: string
|
||||
code?: string
|
||||
prefix: PrefixType
|
||||
}
|
||||
100
types/global.d.ts
vendored
Normal file
100
types/global.d.ts
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
import type { ComponentPublicInstance, FunctionalComponent } from 'vue'
|
||||
|
||||
import type { ContainsDash } from '@/utils/use-point/index'
|
||||
declare global {
|
||||
type Recordable<T = any> = Record<string, T>
|
||||
type TOption = { label: string; value: string | number }
|
||||
type HTMLElementEvent<T extends HTMLElement> = Event & {
|
||||
target: T
|
||||
currentTarget: T
|
||||
}
|
||||
type LoadingType = { show: boolean; type: 'enter' | 'submit' }
|
||||
// vue
|
||||
interface ImportMetaEnv extends ViteEnv {
|
||||
__: unknown
|
||||
}
|
||||
|
||||
interface Array {
|
||||
post?: <T>(data?: Recordable<any>) => Promise<{
|
||||
code: number
|
||||
msg: string
|
||||
data: T
|
||||
}>
|
||||
get?: <T>(data?: Recordable<any>) => Promise<{
|
||||
code: number
|
||||
msg: string
|
||||
data: T
|
||||
}>
|
||||
}
|
||||
|
||||
interface Window {
|
||||
isBgWhite: Ref<boolean>
|
||||
useTipDialog: (
|
||||
callback: (result: boolean) => void | Promise<any>,
|
||||
config: {
|
||||
title: string
|
||||
html: string
|
||||
btnName: string
|
||||
showCloseIcon?: boolean
|
||||
showCancelBtn?: boolean
|
||||
cancelBtnName?: string
|
||||
}
|
||||
) => void
|
||||
useLoading: (ob?: { type: 'enter' | 'submit'; show?: boolean }) => {
|
||||
if: boolean | null
|
||||
type: string
|
||||
show: boolean
|
||||
} & {
|
||||
type: 'enter' | 'submit'
|
||||
show: boolean
|
||||
}
|
||||
useSubmitBtn: (
|
||||
callback: (check: Ref<boolean>) => Promise<boolean> | ToastWrapperInstance,
|
||||
ob?: {
|
||||
type?: 'normal' | 'fixed' | 'fixed-bg' | 'absolute'
|
||||
text: string
|
||||
protocols?: TProtocols
|
||||
id?: string
|
||||
countdownTime?: number
|
||||
checkFn?: () => void
|
||||
protocolFn?: () => void
|
||||
check?: Ref<boolean>
|
||||
}
|
||||
) => {
|
||||
check: Ref<boolean>
|
||||
base: {
|
||||
show: boolean
|
||||
type: any
|
||||
text: string
|
||||
protocols?: TProtocols
|
||||
checkFn?: () => void
|
||||
protocolFn?: () => void
|
||||
check?: Ref<boolean>
|
||||
showForceRead: boolean
|
||||
callback: (check: Ref<boolean>) => Promise<boolean> | ToastWrapperInstance
|
||||
}
|
||||
}
|
||||
useProtocolDialog: (config?: {
|
||||
title?: string
|
||||
html?: string
|
||||
protocolName: string
|
||||
protocolFn?: () => void
|
||||
protocolUrl: string
|
||||
callback: (result: boolean) => void
|
||||
}) => void
|
||||
base64: object
|
||||
[key: string]: any
|
||||
point: <T extends string>(str: ContainsDash<T>, EXTEND_DATA?: any) => Promise<void>
|
||||
}
|
||||
|
||||
interface ViteEnv {
|
||||
VITE_APP_CODE: string
|
||||
VITE_APP_HOST_SIGN: string
|
||||
}
|
||||
type Callback = () => any
|
||||
}
|
||||
declare module 'vue' {
|
||||
export type JSXComponent<Props = any> =
|
||||
| { new (): ComponentPublicInstance<Props> }
|
||||
| FunctionalComponent<Props>
|
||||
}
|
||||
@ -26,10 +26,20 @@ export default defineConfig({
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
port: 3000,
|
||||
proxy: {
|
||||
'/test/api-interface': {
|
||||
target: 'https://api.1024api.com', // 后端接口地址
|
||||
changeOrigin: true,
|
||||
rewrite: (path: string) => {
|
||||
return path.replace(/^\/test/, ``)
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||
'#': fileURLToPath(new URL('./types', import.meta.url)),
|
||||
},
|
||||
},
|
||||
css: {
|
||||
|
||||
15
yarn.lock
15
yarn.lock
@ -1552,6 +1552,11 @@ caniuse-lite@^1.0.30001735:
|
||||
resolved "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001737.tgz#8292bb7591932ff09e9a765f12fdf5629a241ccc"
|
||||
integrity sha512-BiloLiXtQNrY5UyF0+1nSJLXUENuhka2pzy2Fx5pGxqavdrxSCW4U6Pn/PoG3Efspi2frRbHpBV2XsrPE6EDlw==
|
||||
|
||||
capture-request-log@^0.0.4:
|
||||
version "0.0.4"
|
||||
resolved "https://registry.npmmirror.com/capture-request-log/-/capture-request-log-0.0.4.tgz#48525dd2772eb5aef1b5ce8efe0df9d346909e44"
|
||||
integrity sha512-qAaJhbmpBe33qVWQuhCSv9PufuplekCHBCzK5hX07M9wOuAFzTTgOUBuwsC/S5tSZ4ZQCKClKXpuu2qW9UjUGQ==
|
||||
|
||||
chalk@^4.0.0:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
|
||||
@ -2336,6 +2341,11 @@ jiti@^2.4.2, jiti@^2.5.1:
|
||||
resolved "https://registry.npmmirror.com/jiti/-/jiti-2.5.1.tgz#bd099c1c2be1c59bbea4e5adcd127363446759d0"
|
||||
integrity sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==
|
||||
|
||||
js-base64@^3.7.8:
|
||||
version "3.7.8"
|
||||
resolved "https://registry.npmmirror.com/js-base64/-/js-base64-3.7.8.tgz#af44496bc09fa178ed9c4adf67eb2b46f5c6d2a4"
|
||||
integrity sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==
|
||||
|
||||
js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
@ -2429,6 +2439,11 @@ lodash.merge@^4.6.2:
|
||||
resolved "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
|
||||
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
|
||||
|
||||
lodash@^4.17.21:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
|
||||
lru-cache@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user