mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-ui.git
synced 2026-11-06 17:09:08 +08:00
refactor: 四端完成 Options API → script setup + vuex → pinia 迁移
- buyer(95) / seller(169) / im(53) / manager(280) 共 597 个组件全部从 Options API 转换为 <script setup> - 四端 vuex 全部移除,状态管理统一迁移至 pinia 4(stores/ 目录) - util.js 等基础设施去除 vm 首参依赖;全局属性($Message/$filters 等)在 script 内改为具名导入 - 被外部 ref 访问的公共组件均已补齐 defineExpose(清单见各端 MIGRATION-VUE3.md) - 各端 eslint 启用 vue/component-api-style 防止新代码回退 Options API - 四端 yarn build 全部通过;迁移约定与注意事项已更新至各端 MIGRATION-VUE3.md
This commit is contained in:
@@ -9,7 +9,8 @@
|
|||||||
| 框架 | vue | ^3.5.35 |
|
| 框架 | vue | ^3.5.35 |
|
||||||
| 编译 | @vue/compiler-sfc | ^3.5.35 |
|
| 编译 | @vue/compiler-sfc | ^3.5.35 |
|
||||||
| 路由 | vue-router | ^4.6.4 |
|
| 路由 | vue-router | ^4.6.4 |
|
||||||
| 状态 | vuex | ^4.1.0 |
|
| 状态 | pinia | ^4.0.3(四端已全部从 vuex 迁移完成) |
|
||||||
|
| 组件写法 | `<script setup>` | 四端 597 个组件已全部完成 Options API → 组合式 API 迁移;各端 eslint 已启用 `vue/component-api-style: ['warn', ['script-setup']]` 防止回退 |
|
||||||
| UI | element-plus | ^2.14.1 |
|
| UI | element-plus | ^2.14.1 |
|
||||||
| 图标 | @element-plus/icons-vue | ^2.3.2 |
|
| 图标 | @element-plus/icons-vue | ^2.3.2 |
|
||||||
| 请求 | axios | ^1.7.9 |
|
| 请求 | axios | ^1.7.9 |
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## 技术栈
|
## 技术栈
|
||||||
|
|
||||||
- Vue 3 + Vue Router 4 + Vuex 4
|
- Vue 3 + Vue Router 4 + Pinia(vuex 已移除,见下文"组合式 API 迁移")
|
||||||
- Element Plus(替代 view-design / iView)
|
- Element Plus(替代 view-design / iView)
|
||||||
- 保留 `$Message` / `$Modal` / `$Notice` 兼容层(`src/utils/message.js`)
|
- 保留 `$Message` / `$Modal` / `$Notice` 兼容层(`src/utils/message.js`)
|
||||||
|
|
||||||
@@ -29,6 +29,18 @@ yarn build
|
|||||||
- `mv-count-down` → 内置倒计时
|
- `mv-count-down` → 内置倒计时
|
||||||
- 秒杀轮播 → 横向滚动列表
|
- 秒杀轮播 → 横向滚动列表
|
||||||
|
|
||||||
|
## 组合式 API 迁移(2026-10 完成)
|
||||||
|
|
||||||
|
全部 95 个 `.vue` 组件已从 Options API 转换为 `<script setup>`,同时完成 vuex → pinia:
|
||||||
|
|
||||||
|
- **状态管理**:`src/stores/app.js`(`useAppStore`:navList / cartNum / unreadMsg / logoImg / siteName / hotWordsList / category + 对应 setXxx actions)。原 `src/vuex/` 已删除,package.json 不再依赖 vuex。
|
||||||
|
- **路由守卫**:`beforeRouteEnter` 无组合式等价物,5 个页面(`GoodsList` / `GoodsDetail` / `PointGoodsDetail` / `Cart` / `ShoppingCart`)保留普通 `<script>` 块存放 `export default { name, beforeRouteEnter }`——这是官方支持的合并写法,统计 Options API 残留时需排除。
|
||||||
|
- **defineExpose**:以下组件的方法/状态被父组件通过模板 ref 调用,已显式暴露:`verify`(init / close)、`multiple-map`(open)、`addressManage`(show / hide)、`invoiceModal`(invoiceAvailable)。新增此类父子调用时必须加 `defineExpose`。
|
||||||
|
- **模板 ref 与响应式变量同名会互相覆盖**:同名时模板 ref 统一改名 `xxxRef`(如 `formRef` / `verifyRef`)。
|
||||||
|
- **mixin 已消除**:原 `components/mixes/talkIm.vue` 的 IM 客服逻辑已内联进 `Merchant.vue` 和 `goodsDetail/StoreSide.vue`,文件已删除。
|
||||||
|
- **全局属性照旧**:模板中的 `$filters.*` / `linkTo` / `connectCs` / `$inputMaxLength` 是 `main.js` 注册的全局属性,`<script setup>` 下模板可直接使用;script 内使用 `$filters` 函数需从 `@/plugins/filters.js` 具名导入,`this.Cookies` 等同 `storage`(`@/plugins/storage`)。
|
||||||
|
- **其余三端(seller / manager / im)仍为 Options API + vuex**,迁移时参照本端模式。
|
||||||
|
|
||||||
## 批量脚本
|
## 批量脚本
|
||||||
|
|
||||||
- `scripts/migrate-iview-to-element.js` — iView → Element 模板替换
|
- `scripts/migrate-iview-to-element.js` — iView → Element 模板替换
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ module.exports = [
|
|||||||
'vue/multi-word-component-names': 'off',
|
'vue/multi-word-component-names': 'off',
|
||||||
// 存量代码大量使用与 HTML 保留名同名的组件(element-plus 图标 Search/View/Camera/Link 及全局注册的 empty/drawer 等),重命名成本高,暂不启用
|
// 存量代码大量使用与 HTML 保留名同名的组件(element-plus 图标 Search/View/Camera/Link 及全局注册的 empty/drawer 等),重命名成本高,暂不启用
|
||||||
'vue/no-reserved-component-names': 'off',
|
'vue/no-reserved-component-names': 'off',
|
||||||
|
// 全端已完成 Options API → <script setup> 迁移,防止新代码回退(路由守卫的普通 script 块等有意保留的除外,故用 warn)
|
||||||
|
'vue/component-api-style': ['warn', ['script-setup']],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
"element-plus": "^2.14.1",
|
"element-plus": "^2.14.1",
|
||||||
"js-cookie": "^2.2.1",
|
"js-cookie": "^2.2.1",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
|
"pinia": "^4.0.3",
|
||||||
"psl": "^1.8.0",
|
"psl": "^1.8.0",
|
||||||
"qs": "^6.9.4",
|
"qs": "^6.9.4",
|
||||||
"sass": "^1.63.6",
|
"sass": "^1.63.6",
|
||||||
@@ -28,8 +29,7 @@
|
|||||||
"uuid": "^8.3.2",
|
"uuid": "^8.3.2",
|
||||||
"vue": "^3.5.35",
|
"vue": "^3.5.35",
|
||||||
"vue-qr": "^5.0.3",
|
"vue-qr": "^5.0.3",
|
||||||
"vue-router": "^4.6.4",
|
"vue-router": "^4.6.4"
|
||||||
"vuex": "^4.1.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "^5.0.9",
|
"@vue/cli-plugin-babel": "^5.0.9",
|
||||||
@@ -41,6 +41,7 @@
|
|||||||
"globals": "^17.13.0",
|
"globals": "^17.13.0",
|
||||||
"less": "^4.2.0",
|
"less": "^4.2.0",
|
||||||
"less-loader": "^11.1.4",
|
"less-loader": "^11.1.4",
|
||||||
|
"vue-eslint-parser": "^10.4.1",
|
||||||
"webpack": "^5.95.0"
|
"webpack": "^5.95.0"
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
|
|||||||
@@ -4,71 +4,78 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { onMounted } from "vue";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
import storage from "@/plugins/storage";
|
import storage from "@/plugins/storage";
|
||||||
import { getBaseSite, getThemeSetting } from "@/api/common.js";
|
import { getBaseSite, getThemeSetting } from "@/api/common.js";
|
||||||
import { fetchAndApplyTheme } from "@/utils/theme";
|
import { fetchAndApplyTheme } from "@/utils/theme";
|
||||||
|
import { useAppStore } from "@/stores/app";
|
||||||
|
|
||||||
export default {
|
defineOptions({ name: "App" });
|
||||||
name: "App",
|
|
||||||
mounted() {
|
const route = useRoute();
|
||||||
this.init();
|
const appStore = useAppStore();
|
||||||
},
|
|
||||||
methods: {
|
function applySiteInfo(data) {
|
||||||
applySiteInfo(data) {
|
const expirationTime = new Date().setHours(new Date().getHours() + 1);
|
||||||
const expirationTime = new Date().setHours(new Date().getHours() + 1);
|
storage.setItem("sitelogo_expiration_time", expirationTime);
|
||||||
storage.setItem("sitelogo_expiration_time", expirationTime);
|
storage.setItem("siteName", data.siteName);
|
||||||
storage.setItem("siteName", data.siteName);
|
storage.setItem("logoImg", data.buyerSideLogo);
|
||||||
storage.setItem("logoImg", data.buyerSideLogo);
|
storage.setItem("siteIcon", data.buyerSideIcon);
|
||||||
storage.setItem("siteIcon", data.buyerSideIcon);
|
appStore.setSiteName(data.siteName);
|
||||||
this.$store.commit("SET_SITENAME", data.siteName);
|
appStore.setLogoImg(data.buyerSideLogo);
|
||||||
this.$store.commit("SET_LOGOIMG", data.buyerSideLogo);
|
if (route.meta.title === undefined) {
|
||||||
if (this.$route.meta.title === undefined) {
|
window.document.title = data.siteName;
|
||||||
window.document.title = data.siteName;
|
}
|
||||||
|
let link =
|
||||||
|
document.querySelector("link[rel*='icon']") ||
|
||||||
|
document.createElement("link");
|
||||||
|
link.type = "image/x-icon";
|
||||||
|
link.href = data.buyerSideIcon;
|
||||||
|
link.rel = "shortcut icon";
|
||||||
|
document.getElementsByTagName("head")[0].appendChild(link);
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
const expirationTime = storage.getItem("sitelogo_expiration_time");
|
||||||
|
const cacheValid =
|
||||||
|
expirationTime && new Date() <= new Date(Number(expirationTime));
|
||||||
|
if (cacheValid) {
|
||||||
|
applySiteInfo({
|
||||||
|
siteName: storage.getItem("siteName"),
|
||||||
|
buyerSideLogo: storage.getItem("logoImg"),
|
||||||
|
buyerSideIcon: storage.getItem("siteIcon"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
getSite();
|
||||||
|
getTheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSite() {
|
||||||
|
getBaseSite().then((res) => {
|
||||||
|
if (res.success && res.result.settingValue) {
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(res.result.settingValue);
|
||||||
|
applySiteInfo(data);
|
||||||
|
} catch {
|
||||||
|
// 站点配置解析失败时使用缓存
|
||||||
}
|
}
|
||||||
let link =
|
}
|
||||||
document.querySelector("link[rel*='icon']") ||
|
}).catch((err) => {
|
||||||
document.createElement("link");
|
console.warn('获取站点配置失败', err);
|
||||||
link.type = "image/x-icon";
|
});
|
||||||
link.href = data.buyerSideIcon;
|
}
|
||||||
link.rel = "shortcut icon";
|
|
||||||
document.getElementsByTagName("head")[0].appendChild(link);
|
function getTheme() {
|
||||||
},
|
fetchAndApplyTheme(getThemeSetting).catch((err) => {
|
||||||
init() {
|
console.warn('获取主题配置失败', err);
|
||||||
const expirationTime = storage.getItem("sitelogo_expiration_time");
|
});
|
||||||
const cacheValid =
|
}
|
||||||
expirationTime && new Date() <= new Date(Number(expirationTime));
|
|
||||||
if (cacheValid) {
|
onMounted(() => {
|
||||||
this.applySiteInfo({
|
init();
|
||||||
siteName: storage.getItem("siteName"),
|
});
|
||||||
buyerSideLogo: storage.getItem("logoImg"),
|
|
||||||
buyerSideIcon: storage.getItem("siteIcon"),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.getSite();
|
|
||||||
this.getTheme();
|
|
||||||
},
|
|
||||||
getSite() {
|
|
||||||
getBaseSite().then((res) => {
|
|
||||||
if (res.success && res.result.settingValue) {
|
|
||||||
try {
|
|
||||||
const data = JSON.parse(res.result.settingValue);
|
|
||||||
this.applySiteInfo(data);
|
|
||||||
} catch {
|
|
||||||
// 站点配置解析失败时使用缓存
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).catch((err) => {
|
|
||||||
console.warn('获取站点配置失败', err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getTheme() {
|
|
||||||
fetchAndApplyTheme(getThemeSetting).catch((err) => {
|
|
||||||
console.warn('获取主题配置失败', err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
#app {
|
#app {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="navbar" :class="{'small-search-box': useClass == 'fixed-show'}">
|
<div class="navbar" ref="rootRef" :class="{'small-search-box': useClass == 'fixed-show'}">
|
||||||
<template v-if="useClass == 'fixed-show'">
|
<template v-if="useClass == 'fixed-show'">
|
||||||
<div class="search search-embedded">
|
<div class="search search-embedded">
|
||||||
<el-input
|
<el-input
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="container width_1200_auto flex flex-a-c">
|
<div class="container width_1200_auto flex flex-a-c">
|
||||||
<img
|
<img
|
||||||
:src="$store.state.logoImg"
|
:src="logoImg"
|
||||||
v-if="showLogo"
|
v-if="showLogo"
|
||||||
class="logo-img"
|
class="logo-img"
|
||||||
alt=""
|
alt=""
|
||||||
@@ -80,11 +80,45 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import storage from '@/plugins/storage.js'
|
import storage from '@/plugins/storage.js'
|
||||||
import { goodsList, hotWords } from '@/api/goods.js'
|
import { goodsList, hotWords } from '@/api/goods.js'
|
||||||
import { Search as SearchIcon } from '@element-plus/icons-vue'
|
import { Search as SearchIcon } from '@element-plus/icons-vue'
|
||||||
import SearchSuggest from '@/components/SearchSuggest.vue'
|
import SearchSuggest from '@/components/SearchSuggest.vue'
|
||||||
|
import { useAppStore } from '@/stores/app'
|
||||||
|
|
||||||
|
defineOptions({ name: 'search' })
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
showTag: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
showLogo: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
storeId: {
|
||||||
|
type: String,
|
||||||
|
default: ""
|
||||||
|
},
|
||||||
|
hover: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
useClass: {
|
||||||
|
type: null,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['search'])
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
const appStore = useAppStore()
|
||||||
|
|
||||||
const HISTORY_KEY = 'buyerSearchHistory'
|
const HISTORY_KEY = 'buyerSearchHistory'
|
||||||
const HISTORY_LIMIT = 10
|
const HISTORY_LIMIT = 10
|
||||||
@@ -112,179 +146,166 @@ function parseHotWords(raw) {
|
|||||||
return raw.split(',').map((item) => item.trim()).filter(Boolean)
|
return raw.split(',').map((item) => item.trim()).filter(Boolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
const rootRef = ref(null)
|
||||||
name: 'search',
|
const searchData = ref('')
|
||||||
components: { SearchIcon, SearchSuggest },
|
const history = ref([])
|
||||||
props: {
|
const panelVisible = ref(false)
|
||||||
showTag: {
|
const suggestions = ref([])
|
||||||
type: Boolean,
|
const suggesting = ref(false)
|
||||||
default: true
|
let suggestTimer = null
|
||||||
},
|
let suggestSeq = 0
|
||||||
showLogo: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
storeId: {
|
|
||||||
type: String,
|
|
||||||
default: ""
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
useClass:{
|
|
||||||
type:null,
|
|
||||||
default:''
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
searchData: '',
|
|
||||||
history: [],
|
|
||||||
panelVisible: false,
|
|
||||||
suggestions: [],
|
|
||||||
suggesting: false,
|
|
||||||
suggestTimer: null,
|
|
||||||
suggestSeq: 0,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
selectTags(item) {
|
|
||||||
this.searchData = item;
|
|
||||||
this.search();
|
|
||||||
},
|
|
||||||
remember(keyword) {
|
|
||||||
const next = [keyword, ...this.history.filter((item) => item !== keyword)].slice(0, HISTORY_LIMIT)
|
|
||||||
this.history = next
|
|
||||||
localStorage.setItem(HISTORY_KEY, JSON.stringify(next))
|
|
||||||
},
|
|
||||||
clearHistory() {
|
|
||||||
this.history = []
|
|
||||||
localStorage.removeItem(HISTORY_KEY)
|
|
||||||
},
|
|
||||||
openPanel() {
|
|
||||||
this.panelVisible = true
|
|
||||||
if (this.trimmedKeyword) this.queueSuggest()
|
|
||||||
},
|
|
||||||
closePanel() {
|
|
||||||
this.panelVisible = false
|
|
||||||
},
|
|
||||||
onDocumentClick(event) {
|
|
||||||
if (this.$el && !this.$el.contains(event.target)) this.closePanel()
|
|
||||||
},
|
|
||||||
queueSuggest() {
|
|
||||||
this.panelVisible = true
|
|
||||||
clearTimeout(this.suggestTimer)
|
|
||||||
const keyword = this.trimmedKeyword
|
|
||||||
if (!keyword) {
|
|
||||||
this.suggestions = []
|
|
||||||
this.suggesting = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.suggesting = true
|
|
||||||
this.suggestTimer = setTimeout(() => this.fetchSuggest(keyword), 300)
|
|
||||||
},
|
|
||||||
fetchSuggest(keyword) {
|
|
||||||
const seq = ++this.suggestSeq
|
|
||||||
const params = { keyword, pageNumber: 1, pageSize: 8 }
|
|
||||||
if (this.storeId) params.storeId = this.storeId
|
|
||||||
goodsList(params).then((res) => {
|
|
||||||
if (seq !== this.suggestSeq || keyword !== this.trimmedKeyword) return
|
|
||||||
const records = res && res.success ? (res.result.records || []) : []
|
|
||||||
const seen = new Set()
|
|
||||||
const names = []
|
|
||||||
this.promotionTags.forEach((word) => {
|
|
||||||
if (word && String(word).toLowerCase().includes(keyword.toLowerCase()) && !seen.has(word)) {
|
|
||||||
seen.add(word)
|
|
||||||
names.push(word)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
records.forEach((item) => {
|
|
||||||
const name = item && item.goodsName
|
|
||||||
if (name && !seen.has(name)) {
|
|
||||||
seen.add(name)
|
|
||||||
names.push(name)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
this.suggestions = names.slice(0, 8)
|
|
||||||
}).catch(() => {
|
|
||||||
if (seq === this.suggestSeq) this.suggestions = []
|
|
||||||
}).finally(() => {
|
|
||||||
if (seq === this.suggestSeq) this.suggesting = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
search () {
|
|
||||||
const keyword = this.trimmedKeyword
|
|
||||||
this.searchData = keyword
|
|
||||||
this.closePanel()
|
|
||||||
if (keyword) this.remember(keyword)
|
|
||||||
const url = this.$route.path;
|
|
||||||
if(url == '/goodsList'){
|
|
||||||
this.$emit('search', keyword)
|
|
||||||
}else{
|
|
||||||
const pushData = {
|
|
||||||
path:'/goodsList',
|
|
||||||
query: { keyword },
|
|
||||||
}
|
|
||||||
if(this.storeId) pushData.query.storeId = this.storeId
|
|
||||||
|
|
||||||
this.$router.push(pushData);
|
const logoImg = computed(() => appStore.logoImg)
|
||||||
}
|
|
||||||
},
|
|
||||||
searchStore() {
|
|
||||||
this.$emit('search', this.trimmedKeyword)
|
|
||||||
},
|
|
||||||
applyHotWords(result) {
|
|
||||||
const list = parseHotWords(result)
|
|
||||||
this.$store.commit('SET_HOTWORDS', list)
|
|
||||||
storage.setItem('hotWordsList', JSON.stringify(list))
|
|
||||||
},
|
|
||||||
loadHotWords() {
|
|
||||||
hotWords({ count: 5 }).then((res) => {
|
|
||||||
if (res.success && res.result) this.applyHotWords(res.result)
|
|
||||||
}).catch(() => {
|
|
||||||
// 热词为可选数据,失败时沿用缓存或默认热词
|
|
||||||
})
|
|
||||||
storage.setItem('hotWordsReloadTime', new Date().getTime())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
trimmedKeyword() {
|
|
||||||
return (this.searchData || '').trim()
|
|
||||||
},
|
|
||||||
promotionTags() {
|
|
||||||
return parseHotWords(this.$store.state.hotWordsList)
|
|
||||||
},
|
|
||||||
showPanel() {
|
|
||||||
if (!this.panelVisible) return false
|
|
||||||
if (this.trimmedKeyword) return true
|
|
||||||
return this.history.length > 0 || this.promotionTags.length > 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
'$route.query.keyword'(value) {
|
|
||||||
this.searchData = value || ''
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.searchData = this.$route.query.keyword || ''
|
|
||||||
this.history = readHistory()
|
|
||||||
|
|
||||||
if (!this.hover) {
|
const trimmedKeyword = computed(() => {
|
||||||
const reloadTime = storage.getItem('hotWordsReloadTime')
|
return (searchData.value || '').trim()
|
||||||
const time = new Date().getTime() - 5 * 60 * 1000
|
})
|
||||||
if (!reloadTime || time > reloadTime) this.loadHotWords()
|
|
||||||
}
|
const promotionTags = computed(() => {
|
||||||
},
|
return parseHotWords(appStore.hotWordsList)
|
||||||
mounted() {
|
})
|
||||||
this.onDocumentClick = this.onDocumentClick.bind(this)
|
|
||||||
document.addEventListener('click', this.onDocumentClick)
|
const showPanel = computed(() => {
|
||||||
},
|
if (!panelVisible.value) return false
|
||||||
beforeUnmount() {
|
if (trimmedKeyword.value) return true
|
||||||
clearTimeout(this.suggestTimer)
|
return history.value.length > 0 || promotionTags.value.length > 0
|
||||||
document.removeEventListener('click', this.onDocumentClick)
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => route.query.keyword,
|
||||||
|
(value) => {
|
||||||
|
searchData.value = value || ''
|
||||||
}
|
}
|
||||||
};
|
)
|
||||||
|
|
||||||
|
function selectTags(item) {
|
||||||
|
searchData.value = item
|
||||||
|
search()
|
||||||
|
}
|
||||||
|
|
||||||
|
function remember(keyword) {
|
||||||
|
const next = [keyword, ...history.value.filter((item) => item !== keyword)].slice(0, HISTORY_LIMIT)
|
||||||
|
history.value = next
|
||||||
|
localStorage.setItem(HISTORY_KEY, JSON.stringify(next))
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearHistory() {
|
||||||
|
history.value = []
|
||||||
|
localStorage.removeItem(HISTORY_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
|
function openPanel() {
|
||||||
|
panelVisible.value = true
|
||||||
|
if (trimmedKeyword.value) queueSuggest()
|
||||||
|
}
|
||||||
|
|
||||||
|
function closePanel() {
|
||||||
|
panelVisible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDocumentClick(event) {
|
||||||
|
if (rootRef.value && !rootRef.value.contains(event.target)) closePanel()
|
||||||
|
}
|
||||||
|
|
||||||
|
function queueSuggest() {
|
||||||
|
panelVisible.value = true
|
||||||
|
clearTimeout(suggestTimer)
|
||||||
|
const keyword = trimmedKeyword.value
|
||||||
|
if (!keyword) {
|
||||||
|
suggestions.value = []
|
||||||
|
suggesting.value = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
suggesting.value = true
|
||||||
|
suggestTimer = setTimeout(() => fetchSuggest(keyword), 300)
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchSuggest(keyword) {
|
||||||
|
const seq = ++suggestSeq
|
||||||
|
const params = { keyword, pageNumber: 1, pageSize: 8 }
|
||||||
|
if (props.storeId) params.storeId = props.storeId
|
||||||
|
goodsList(params).then((res) => {
|
||||||
|
if (seq !== suggestSeq || keyword !== trimmedKeyword.value) return
|
||||||
|
const records = res && res.success ? (res.result.records || []) : []
|
||||||
|
const seen = new Set()
|
||||||
|
const names = []
|
||||||
|
promotionTags.value.forEach((word) => {
|
||||||
|
if (word && String(word).toLowerCase().includes(keyword.toLowerCase()) && !seen.has(word)) {
|
||||||
|
seen.add(word)
|
||||||
|
names.push(word)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
records.forEach((item) => {
|
||||||
|
const name = item && item.goodsName
|
||||||
|
if (name && !seen.has(name)) {
|
||||||
|
seen.add(name)
|
||||||
|
names.push(name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
suggestions.value = names.slice(0, 8)
|
||||||
|
}).catch(() => {
|
||||||
|
if (seq === suggestSeq) suggestions.value = []
|
||||||
|
}).finally(() => {
|
||||||
|
if (seq === suggestSeq) suggesting.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function search() {
|
||||||
|
const keyword = trimmedKeyword.value
|
||||||
|
searchData.value = keyword
|
||||||
|
closePanel()
|
||||||
|
if (keyword) remember(keyword)
|
||||||
|
const url = route.path
|
||||||
|
if (url == '/goodsList') {
|
||||||
|
emit('search', keyword)
|
||||||
|
} else {
|
||||||
|
const pushData = {
|
||||||
|
path: '/goodsList',
|
||||||
|
query: { keyword },
|
||||||
|
}
|
||||||
|
if (props.storeId) pushData.query.storeId = props.storeId
|
||||||
|
|
||||||
|
router.push(pushData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function searchStore() {
|
||||||
|
emit('search', trimmedKeyword.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyHotWords(result) {
|
||||||
|
const list = parseHotWords(result)
|
||||||
|
appStore.setHotWords(list)
|
||||||
|
storage.setItem('hotWordsList', JSON.stringify(list))
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadHotWords() {
|
||||||
|
hotWords({ count: 5 }).then((res) => {
|
||||||
|
if (res.success && res.result) applyHotWords(res.result)
|
||||||
|
}).catch(() => {
|
||||||
|
// 热词为可选数据,失败时沿用缓存或默认热词
|
||||||
|
})
|
||||||
|
storage.setItem('hotWordsReloadTime', new Date().getTime())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化(原 created 钩子逻辑)
|
||||||
|
searchData.value = route.query.keyword || ''
|
||||||
|
history.value = readHistory()
|
||||||
|
|
||||||
|
if (!props.hover) {
|
||||||
|
const reloadTime = storage.getItem('hotWordsReloadTime')
|
||||||
|
const time = new Date().getTime() - 5 * 60 * 1000
|
||||||
|
if (!reloadTime || time > reloadTime) loadHotWords()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
document.addEventListener('click', onDocumentClick)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
clearTimeout(suggestTimer)
|
||||||
|
document.removeEventListener('click', onDocumentClick)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.navbar {
|
.navbar {
|
||||||
|
|||||||
@@ -45,32 +45,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
defineOptions({ name: "SearchSuggest" });
|
||||||
name: "SearchSuggest",
|
|
||||||
props: {
|
const props = defineProps({
|
||||||
keyword: { type: String, default: "" },
|
keyword: { type: String, default: "" },
|
||||||
history: { type: Array, default: () => [] },
|
history: { type: Array, default: () => [] },
|
||||||
hotWords: { type: Array, default: () => [] },
|
hotWords: { type: Array, default: () => [] },
|
||||||
suggestions: { type: Array, default: () => [] },
|
suggestions: { type: Array, default: () => [] },
|
||||||
suggesting: { type: Boolean, default: false },
|
suggesting: { type: Boolean, default: false },
|
||||||
},
|
});
|
||||||
emits: ["pick", "clear"],
|
|
||||||
methods: {
|
defineEmits(["pick", "clear"]);
|
||||||
parts(text) {
|
|
||||||
const value = String(text || "");
|
function parts(text) {
|
||||||
const key = this.keyword;
|
const value = String(text || "");
|
||||||
if (!key) return [{ text: value, hit: false }];
|
const key = props.keyword;
|
||||||
const index = value.toLowerCase().indexOf(key.toLowerCase());
|
if (!key) return [{ text: value, hit: false }];
|
||||||
if (index < 0) return [{ text: value, hit: false }];
|
const index = value.toLowerCase().indexOf(key.toLowerCase());
|
||||||
return [
|
if (index < 0) return [{ text: value, hit: false }];
|
||||||
{ text: value.slice(0, index), hit: false },
|
return [
|
||||||
{ text: value.slice(index, index + key.length), hit: true },
|
{ text: value.slice(0, index), hit: false },
|
||||||
{ text: value.slice(index + key.length), hit: false },
|
{ text: value.slice(index, index + key.length), hit: true },
|
||||||
].filter((part) => part.text);
|
{ text: value.slice(index + key.length), hit: false },
|
||||||
},
|
].filter((part) => part.text);
|
||||||
},
|
}
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<el-dialog v-model="showAddr" width="800" title="收件人地址">
|
<el-dialog v-model="showAddr" width="800" title="收件人地址">
|
||||||
<el-form
|
<el-form
|
||||||
:model="formData"
|
:model="formData"
|
||||||
ref="form"
|
ref="formRef"
|
||||||
label-position="left"
|
label-position="left"
|
||||||
label-width="100px"
|
label-width="100px"
|
||||||
:rules="ruleInline"
|
:rules="ruleInline"
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="收件地区" prop="address">
|
<el-form-item label="收件地区" prop="address">
|
||||||
{{ formData.address || '暂无地址' }}
|
{{ formData.address || '暂无地址' }}
|
||||||
<el-button type="primary" style="margin-left: 10px;" size="small" @click="$refs.map.open()">选择</el-button>
|
<el-button type="primary" style="margin-left: 10px;" size="small" @click="mapRef.open()">选择</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="详细地址" prop="detail">
|
<el-form-item label="详细地址" prop="detail">
|
||||||
<el-input v-model="formData.detail" style="width: 600px"></el-input>
|
<el-input v-model="formData.detail" style="width: 600px"></el-input>
|
||||||
@@ -39,11 +39,12 @@
|
|||||||
<el-button type="primary" class="mr_10" :loading="loading" @click="save">保存收货地址</el-button>
|
<el-button type="primary" class="mr_10" :loading="loading" @click="save">保存收货地址</el-button>
|
||||||
</div></template>
|
</div></template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
<multipleMap ref="map" @callback="getAddress"></multipleMap>
|
<multipleMap ref="mapRef" @callback="getAddress"></multipleMap>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { ref, watch } from "vue";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
import multipleMap from "@/components/map/multiple-map";
|
import multipleMap from "@/components/map/multiple-map";
|
||||||
import {
|
import {
|
||||||
@@ -51,122 +52,126 @@ import {
|
|||||||
editMemberAddress,
|
editMemberAddress,
|
||||||
getAddrDetail
|
getAddrDetail
|
||||||
} from '@/api/address';
|
} from '@/api/address';
|
||||||
export default {
|
|
||||||
name: 'addressManage',
|
|
||||||
props: {
|
|
||||||
id: { // 传入的地址id
|
|
||||||
defalut: '',
|
|
||||||
type: String
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
showAddr: false, // 控制模态框显隐
|
|
||||||
formData: { // 表单数据
|
|
||||||
isDefault: false
|
|
||||||
},
|
|
||||||
ruleInline: { // 验证规则
|
|
||||||
name: [{ required: true, message: '请输入收件人姓名', trigger: 'blur' }],
|
|
||||||
address: [{ required: true, message: '请输入地址', trigger: 'change' }],
|
|
||||||
detail: [
|
|
||||||
{ required: true, message: '请输入详细地址', trigger: 'blur' }
|
|
||||||
],
|
|
||||||
mobile: [
|
|
||||||
{ required: true, message: '手机号不能为空', trigger: 'blur' },
|
|
||||||
{
|
|
||||||
type: 'string',
|
|
||||||
pattern: /^1[3|4|5|6|7|8][0-9]{9}$/,
|
|
||||||
message: '手机号格式出错',
|
|
||||||
trigger: 'blur'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
loading: false, // 提交的加载状态
|
|
||||||
mapMsg: {} // 地图信息
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
save () { // 保存地址
|
|
||||||
this.$refs.form.validate((valid) => {
|
|
||||||
if (valid) {
|
|
||||||
const params = JSON.parse(JSON.stringify(this.formData));
|
|
||||||
params.consigneeAddressPath = params.address.replace(/\s/g, ',');
|
|
||||||
delete params.address;
|
|
||||||
this.loading = true;
|
|
||||||
if (this.id) {
|
|
||||||
editMemberAddress(params).then((res) => {
|
|
||||||
this.loading = false;
|
|
||||||
if (res.success) {
|
|
||||||
Message.success('编辑地址成功');
|
|
||||||
this.$emit('change', true);
|
|
||||||
this.hide();
|
|
||||||
}
|
|
||||||
}).catch(() => { this.loading = false; });
|
|
||||||
} else {
|
|
||||||
newMemberAddress(params).then((res) => {
|
|
||||||
this.loading = false;
|
|
||||||
if (res.success) {
|
|
||||||
Message.success('新增地址成功');
|
|
||||||
this.$emit('change', true);
|
|
||||||
this.hide();
|
|
||||||
}
|
|
||||||
}).catch(() => { this.loading = false; });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getAddrById (id) {
|
|
||||||
// 获取地址详情
|
|
||||||
getAddrDetail(id).then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
const data = res.result;
|
|
||||||
data.address = res.result.consigneeAddressPath.replace(/,/g, ' ');
|
|
||||||
this.formData = data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getAddress (val) {
|
|
||||||
// 获取地图选择信息
|
|
||||||
if(val.type === 'select'){
|
|
||||||
const paths = val.data.map(item => item.name).join(',')
|
|
||||||
const ids = val.data.map(item => item.id).join(',')
|
|
||||||
this.formData['address'] = paths
|
|
||||||
this.formData['consigneeAddressIdPath'] = ids
|
|
||||||
const coord = val.data[val.data.length - 1].center.split(',')
|
|
||||||
this.formData.lat = coord[1]
|
|
||||||
this.formData.lon = coord[0]
|
|
||||||
}else{
|
|
||||||
this.formData["address"] = val.data.addr;
|
|
||||||
this.formData["consigneeAddressIdPath"] = val.data.addrId;
|
|
||||||
this.formData["detail"] = val.data.address;
|
|
||||||
this.formData.lat = val.data.position.lat;
|
|
||||||
this.formData.lon = val.data.position.lng;
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
defineOptions({ name: 'addressManage' });
|
||||||
show () { // 地址模态框显示
|
|
||||||
this.showAddr = true;
|
const props = defineProps({
|
||||||
},
|
id: { // 传入的地址id
|
||||||
hide () { // 地址模态框隐藏
|
defalut: '',
|
||||||
this.showAddr = false;
|
type: String
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['change']);
|
||||||
|
|
||||||
|
const showAddr = ref(false); // 控制模态框显隐
|
||||||
|
const formData = ref({ // 表单数据
|
||||||
|
isDefault: false
|
||||||
|
});
|
||||||
|
const ruleInline = { // 验证规则
|
||||||
|
name: [{ required: true, message: '请输入收件人姓名', trigger: 'blur' }],
|
||||||
|
address: [{ required: true, message: '请输入地址', trigger: 'change' }],
|
||||||
|
detail: [
|
||||||
|
{ required: true, message: '请输入详细地址', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
mobile: [
|
||||||
|
{ required: true, message: '手机号不能为空', trigger: 'blur' },
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
pattern: /^1[3|4|5|6|7|8][0-9]{9}$/,
|
||||||
|
message: '手机号格式出错',
|
||||||
|
trigger: 'blur'
|
||||||
}
|
}
|
||||||
},
|
]
|
||||||
watch: {
|
};
|
||||||
id: { // 传入的地址id
|
const loading = ref(false); // 提交的加载状态
|
||||||
handler: function (v) {
|
const mapMsg = ref({}); // 地图信息
|
||||||
if (v) {
|
|
||||||
this.getAddrById(v);
|
const formRef = ref(null);
|
||||||
} else {
|
const mapRef = ref(null);
|
||||||
this.formData = {isDefault: false}
|
|
||||||
this.$refs.form.resetFields();
|
function save () { // 保存地址
|
||||||
}
|
formRef.value.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
const params = JSON.parse(JSON.stringify(formData.value));
|
||||||
|
params.consigneeAddressPath = params.address.replace(/\s/g, ',');
|
||||||
|
delete params.address;
|
||||||
|
loading.value = true;
|
||||||
|
if (props.id) {
|
||||||
|
editMemberAddress(params).then((res) => {
|
||||||
|
loading.value = false;
|
||||||
|
if (res.success) {
|
||||||
|
Message.success('编辑地址成功');
|
||||||
|
emit('change', true);
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
}).catch(() => { loading.value = false; });
|
||||||
|
} else {
|
||||||
|
newMemberAddress(params).then((res) => {
|
||||||
|
loading.value = false;
|
||||||
|
if (res.success) {
|
||||||
|
Message.success('新增地址成功');
|
||||||
|
emit('change', true);
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
}).catch(() => { loading.value = false; });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
components: {
|
}
|
||||||
multipleMap
|
|
||||||
|
function getAddrById (id) {
|
||||||
|
// 获取地址详情
|
||||||
|
getAddrDetail(id).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
const data = res.result;
|
||||||
|
data.address = res.result.consigneeAddressPath.replace(/,/g, ' ');
|
||||||
|
formData.value = data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAddress (val) {
|
||||||
|
// 获取地图选择信息
|
||||||
|
if(val.type === 'select'){
|
||||||
|
const paths = val.data.map(item => item.name).join(',')
|
||||||
|
const ids = val.data.map(item => item.id).join(',')
|
||||||
|
formData.value['address'] = paths
|
||||||
|
formData.value['consigneeAddressIdPath'] = ids
|
||||||
|
const coord = val.data[val.data.length - 1].center.split(',')
|
||||||
|
formData.value.lat = coord[1]
|
||||||
|
formData.value.lon = coord[0]
|
||||||
|
}else{
|
||||||
|
formData.value["address"] = val.data.addr;
|
||||||
|
formData.value["consigneeAddressIdPath"] = val.data.addrId;
|
||||||
|
formData.value["detail"] = val.data.address;
|
||||||
|
formData.value.lat = val.data.position.lat;
|
||||||
|
formData.value.lon = val.data.position.lng;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function show () { // 地址模态框显示
|
||||||
|
showAddr.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hide () { // 地址模态框隐藏
|
||||||
|
showAddr.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ show, hide });
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.id, // 传入的地址id
|
||||||
|
(v) => {
|
||||||
|
if (v) {
|
||||||
|
getAddrById(v);
|
||||||
|
} else {
|
||||||
|
formData.value = {isDefault: false}
|
||||||
|
formRef.value.resetFields();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -5,22 +5,18 @@
|
|||||||
<el-icon :size="20" @click="show = false"><CircleClose /></el-icon>
|
<el-icon :size="20" @click="show = false"><CircleClose /></el-icon>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
import { CircleClose } from '@element-plus/icons-vue';
|
import { CircleClose } from '@element-plus/icons-vue';
|
||||||
export default {
|
|
||||||
components: { CircleClose },
|
defineProps({
|
||||||
props: {
|
data: { // 传入的广告信息
|
||||||
data: { // 传入的广告信息
|
type: Object,
|
||||||
type: Object,
|
default: null
|
||||||
default: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
show: true // 是否显示头部广告
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
|
const show = ref(true); // 是否显示头部广告
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.advertising {
|
.advertising {
|
||||||
|
|||||||
@@ -26,57 +26,62 @@
|
|||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
import { ref, watch } from "vue";
|
||||||
name: "index",
|
import { useRouter } from "vue-router";
|
||||||
props: {
|
|
||||||
_Tabs: {
|
defineOptions({ name: "index" });
|
||||||
type: null,
|
|
||||||
default: "",
|
const props = defineProps({
|
||||||
},
|
_Tabs: {
|
||||||
_Title: {
|
type: null,
|
||||||
type: null,
|
default: "",
|
||||||
default: "卡片头部",
|
|
||||||
},
|
|
||||||
_More: {
|
|
||||||
type: null,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
_Size: {
|
|
||||||
type: Number,
|
|
||||||
default: 16,
|
|
||||||
},
|
|
||||||
_Src: {
|
|
||||||
type: null,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
_ActiveTab: {
|
|
||||||
type: Number,
|
|
||||||
default: 0,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
data() {
|
_Title: {
|
||||||
return {
|
type: null,
|
||||||
isActive: this._ActiveTab,
|
default: "卡片头部",
|
||||||
};
|
|
||||||
},
|
},
|
||||||
watch: {
|
_More: {
|
||||||
_ActiveTab(val) {
|
type: null,
|
||||||
this.isActive = val;
|
default: false,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
methods: {
|
_Size: {
|
||||||
callBack() {
|
type: Number,
|
||||||
if (this._Src) {
|
default: 16,
|
||||||
this.$router.push({ path: this._Src });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tabsChange(index) {
|
|
||||||
this.isActive = index;
|
|
||||||
this.$emit("_Change", index);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
_Src: {
|
||||||
|
type: null,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
_ActiveTab: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(["_Change"]);
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const isActive = ref(props._ActiveTab);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props._ActiveTab,
|
||||||
|
(val) => {
|
||||||
|
isActive.value = val;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
function callBack() {
|
||||||
|
if (props._Src) {
|
||||||
|
router.push({ path: props._Src });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tabsChange(index) {
|
||||||
|
isActive.value = index;
|
||||||
|
emit("_Change", index);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -78,227 +78,228 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { ref, watch } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
import { Message, Modal } from "@/utils/message";
|
import { Message, Modal } from "@/utils/message";
|
||||||
import { Delete } from '@element-plus/icons-vue';
|
import { Delete } from '@element-plus/icons-vue';
|
||||||
import {cartGoodsAll, delCartGoods, addCartGoods, cartCount} from '@/api/cart.js'
|
import {cartGoodsAll, delCartGoods, addCartGoods, cartCount} from '@/api/cart.js'
|
||||||
import { getOrderList } from '@/api/order';
|
import { getOrderList as fetchOrderList } from '@/api/order';
|
||||||
import {couponList, receiveCoupon, tracksList, collectList, cancelCollect} from '@/api/member.js'
|
import {couponList as fetchCouponList, receiveCoupon, tracksList as fetchTracksList, collectList as fetchCollectList, cancelCollect as apiCancelCollect} from '@/api/member.js'
|
||||||
export default {
|
import storage from '@/plugins/storage.js'
|
||||||
components: { Delete },
|
import { useAppStore } from '@/stores/app'
|
||||||
name: 'Drawer',
|
|
||||||
props: {
|
defineOptions({ name: 'Drawer' })
|
||||||
title: {
|
|
||||||
default: '',
|
const props = defineProps({
|
||||||
type: String
|
title: {
|
||||||
}
|
default: '',
|
||||||
},
|
type: String
|
||||||
watch: {
|
|
||||||
title (val) {
|
|
||||||
switch (val) {
|
|
||||||
case '购物车':
|
|
||||||
this.getCartList()
|
|
||||||
break;
|
|
||||||
case '我的订单':
|
|
||||||
this.getOrderList()
|
|
||||||
break;
|
|
||||||
case '我的足迹':
|
|
||||||
this.getTracksList()
|
|
||||||
break;
|
|
||||||
case '优惠券':
|
|
||||||
this.getCouponList()
|
|
||||||
break;
|
|
||||||
case '我的收藏':
|
|
||||||
this.getCollectList()
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
loading: false, // 控制spin显隐
|
|
||||||
cartList: [], // 购物车列表
|
|
||||||
couponList: [], // 优惠券列表
|
|
||||||
orderList: [], // 订单列表
|
|
||||||
collectList: [], // 收藏列表
|
|
||||||
tracksList: [], // 足迹列表
|
|
||||||
orderStatusList: [ // 订单状态
|
|
||||||
{
|
|
||||||
name: '未付款',
|
|
||||||
status: 'UNPAID'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '已付款',
|
|
||||||
status: 'PAID'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '待发货',
|
|
||||||
status: 'UNDELIVERED'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '已发货',
|
|
||||||
status: 'DELIVERED'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '已完成',
|
|
||||||
status: 'COMPLETED'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '待核验',
|
|
||||||
status: 'TAKE'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '已取消',
|
|
||||||
status: 'CANCELLED'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getCartList () { // 获取购物车列表
|
|
||||||
this.loading = true
|
|
||||||
cartGoodsAll().then(res => {
|
|
||||||
this.loading = false
|
|
||||||
this.cartList = res.result.skuList
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// 删除商品
|
|
||||||
delGoods (id) {
|
|
||||||
delCartGoods({ skuIds: id }).then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
Message.success('删除成功');
|
|
||||||
this.getCartList();
|
|
||||||
cartCount().then(res => {
|
|
||||||
this.$store.commit('SET_CARTNUM', res.result)
|
|
||||||
this.Cookies.setItem('cartNum', res.result)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Message.error(res.message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
filterOrderStatus (status) { // 获取订单状态中文
|
|
||||||
const ob = this.orderStatusList.filter(e => { return e.status === status });
|
|
||||||
return ob[0].name
|
|
||||||
},
|
|
||||||
receive (item) { // 领取优惠券
|
|
||||||
receiveCoupon(item.id).then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
Modal.confirm({
|
|
||||||
title: '领取优惠券',
|
|
||||||
content: '<p>优惠券领取成功,可到我的优惠券页面查看</p>',
|
|
||||||
okText: '我的优惠券',
|
|
||||||
cancelText: '立即使用',
|
|
||||||
onOk: () => {
|
|
||||||
this.$router.push('/home/Coupons')
|
|
||||||
},
|
|
||||||
onCancel: () => {
|
|
||||||
if (item.storeId !== '0') {
|
|
||||||
this.$router.push({path: '/merchant', query: {id: item.storeId}})
|
|
||||||
} else {
|
|
||||||
if (item.scopeType === 'PORTION_GOODS_CATEGORY') {
|
|
||||||
this.$router.push({path: '/goodsList', query: {categoryId: item.scopeId}})
|
|
||||||
} else {
|
|
||||||
this.$router.push({path: '/goodsList'})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
useScope (type, storeName) { // 判断优惠券使用范围
|
|
||||||
let shop = '平台';
|
|
||||||
let goods = '全部商品'
|
|
||||||
if (storeName !== 'platform') shop = storeName
|
|
||||||
switch (type) {
|
|
||||||
case 'ALL':
|
|
||||||
goods = '全部商品'
|
|
||||||
break;
|
|
||||||
case 'PORTION_GOODS':
|
|
||||||
goods = '部分商品'
|
|
||||||
break;
|
|
||||||
case 'PORTION_GOODS_CATEGORY':
|
|
||||||
goods = '部分分类商品'
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return `${shop}${goods}可用`
|
|
||||||
},
|
|
||||||
addToCart (id) { // 添加商品到购物车
|
|
||||||
const params = {
|
|
||||||
num: 1,
|
|
||||||
skuId: id
|
|
||||||
}
|
|
||||||
this.loading = true;
|
|
||||||
addCartGoods(params).then(res => {
|
|
||||||
this.loading = false;
|
|
||||||
if (res.success) {
|
|
||||||
Message.success('商品已成功添加到购物车')
|
|
||||||
} else {
|
|
||||||
Message.warning(res.message);
|
|
||||||
}
|
|
||||||
}).catch(() => { this.loading = false });
|
|
||||||
},
|
|
||||||
getCouponList () { // 获取优惠券列表
|
|
||||||
// this.loading = true;
|
|
||||||
const params = {
|
|
||||||
pageNumber: 1,
|
|
||||||
pageSize: 10
|
|
||||||
}
|
|
||||||
couponList(params).then(res => {
|
|
||||||
this.loading = false
|
|
||||||
if (res.success) {
|
|
||||||
this.couponList = res.result.records
|
|
||||||
}
|
|
||||||
}).catch(() => { this.loading = false })
|
|
||||||
},
|
|
||||||
getOrderList () { // 获取订单列表
|
|
||||||
this.loading = true
|
|
||||||
const params = {
|
|
||||||
pageNumber: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
tag: 'ALL'
|
|
||||||
}
|
|
||||||
getOrderList(params).then(res => {
|
|
||||||
this.loading = false
|
|
||||||
if (res.success) {
|
|
||||||
this.orderList = res.result.records;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getCollectList () { // 获取收藏列表
|
|
||||||
const params = {
|
|
||||||
pageNumber: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
type: 'GOODS'
|
|
||||||
}
|
|
||||||
this.loading = true
|
|
||||||
collectList(params).then(res => {
|
|
||||||
this.loading = false
|
|
||||||
this.collectList = res.result.records
|
|
||||||
})
|
|
||||||
},
|
|
||||||
cancelCollect (id) { // 取消商品收藏
|
|
||||||
cancelCollect('GOODS', id).then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
Message.success('取消收藏成功')
|
|
||||||
this.getCollectList();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
getTracksList () { // 获取足迹列表
|
|
||||||
const params = {
|
|
||||||
pageNumber: 1,
|
|
||||||
pageSize: 20
|
|
||||||
}
|
|
||||||
this.loading = true
|
|
||||||
tracksList(params).then(res => {
|
|
||||||
this.tracksList = res.result.records
|
|
||||||
this.loading = false
|
|
||||||
}).catch(() => { this.loading = false })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
})
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const appStore = useAppStore()
|
||||||
|
|
||||||
|
const loading = ref(false) // 控制spin显隐
|
||||||
|
const cartList = ref([]) // 购物车列表
|
||||||
|
const couponList = ref([]) // 优惠券列表
|
||||||
|
const orderList = ref([]) // 订单列表
|
||||||
|
const collectList = ref([]) // 收藏列表
|
||||||
|
const tracksList = ref([]) // 足迹列表
|
||||||
|
const orderStatusList = ref([ // 订单状态
|
||||||
|
{
|
||||||
|
name: '未付款',
|
||||||
|
status: 'UNPAID'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '已付款',
|
||||||
|
status: 'PAID'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '待发货',
|
||||||
|
status: 'UNDELIVERED'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '已发货',
|
||||||
|
status: 'DELIVERED'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '已完成',
|
||||||
|
status: 'COMPLETED'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '待核验',
|
||||||
|
status: 'TAKE'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '已取消',
|
||||||
|
status: 'CANCELLED'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
watch(() => props.title, (val) => {
|
||||||
|
switch (val) {
|
||||||
|
case '购物车':
|
||||||
|
getCartList()
|
||||||
|
break;
|
||||||
|
case '我的订单':
|
||||||
|
getOrderList()
|
||||||
|
break;
|
||||||
|
case '我的足迹':
|
||||||
|
getTracksList()
|
||||||
|
break;
|
||||||
|
case '优惠券':
|
||||||
|
getCouponList()
|
||||||
|
break;
|
||||||
|
case '我的收藏':
|
||||||
|
getCollectList()
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function getCartList () { // 获取购物车列表
|
||||||
|
loading.value = true
|
||||||
|
cartGoodsAll().then(res => {
|
||||||
|
loading.value = false
|
||||||
|
cartList.value = res.result.skuList
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 删除商品
|
||||||
|
function delGoods (id) {
|
||||||
|
delCartGoods({ skuIds: id }).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
Message.success('删除成功');
|
||||||
|
getCartList();
|
||||||
|
cartCount().then(res => {
|
||||||
|
appStore.setCartNum(res.result)
|
||||||
|
storage.setItem('cartNum', res.result)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Message.error(res.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function filterOrderStatus (status) { // 获取订单状态中文
|
||||||
|
const ob = orderStatusList.value.filter(e => { return e.status === status });
|
||||||
|
return ob[0].name
|
||||||
|
}
|
||||||
|
function receive (item) { // 领取优惠券
|
||||||
|
receiveCoupon(item.id).then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
Modal.confirm({
|
||||||
|
title: '领取优惠券',
|
||||||
|
content: '<p>优惠券领取成功,可到我的优惠券页面查看</p>',
|
||||||
|
okText: '我的优惠券',
|
||||||
|
cancelText: '立即使用',
|
||||||
|
onOk: () => {
|
||||||
|
router.push('/home/Coupons')
|
||||||
|
},
|
||||||
|
onCancel: () => {
|
||||||
|
if (item.storeId !== '0') {
|
||||||
|
router.push({path: '/merchant', query: {id: item.storeId}})
|
||||||
|
} else {
|
||||||
|
if (item.scopeType === 'PORTION_GOODS_CATEGORY') {
|
||||||
|
router.push({path: '/goodsList', query: {categoryId: item.scopeId}})
|
||||||
|
} else {
|
||||||
|
router.push({path: '/goodsList'})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function useScope (type, storeName) { // 判断优惠券使用范围
|
||||||
|
let shop = '平台';
|
||||||
|
let goods = '全部商品'
|
||||||
|
if (storeName !== 'platform') shop = storeName
|
||||||
|
switch (type) {
|
||||||
|
case 'ALL':
|
||||||
|
goods = '全部商品'
|
||||||
|
break;
|
||||||
|
case 'PORTION_GOODS':
|
||||||
|
goods = '部分商品'
|
||||||
|
break;
|
||||||
|
case 'PORTION_GOODS_CATEGORY':
|
||||||
|
goods = '部分分类商品'
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return `${shop}${goods}可用`
|
||||||
|
}
|
||||||
|
function addToCart (id) { // 添加商品到购物车
|
||||||
|
const params = {
|
||||||
|
num: 1,
|
||||||
|
skuId: id
|
||||||
|
}
|
||||||
|
loading.value = true;
|
||||||
|
addCartGoods(params).then(res => {
|
||||||
|
loading.value = false;
|
||||||
|
if (res.success) {
|
||||||
|
Message.success('商品已成功添加到购物车')
|
||||||
|
} else {
|
||||||
|
Message.warning(res.message);
|
||||||
|
}
|
||||||
|
}).catch(() => { loading.value = false });
|
||||||
|
}
|
||||||
|
function getCouponList () { // 获取优惠券列表
|
||||||
|
// loading.value = true;
|
||||||
|
const params = {
|
||||||
|
pageNumber: 1,
|
||||||
|
pageSize: 10
|
||||||
|
}
|
||||||
|
fetchCouponList(params).then(res => {
|
||||||
|
loading.value = false
|
||||||
|
if (res.success) {
|
||||||
|
couponList.value = res.result.records
|
||||||
|
}
|
||||||
|
}).catch(() => { loading.value = false })
|
||||||
|
}
|
||||||
|
function getOrderList () { // 获取订单列表
|
||||||
|
loading.value = true
|
||||||
|
const params = {
|
||||||
|
pageNumber: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
tag: 'ALL'
|
||||||
|
}
|
||||||
|
fetchOrderList(params).then(res => {
|
||||||
|
loading.value = false
|
||||||
|
if (res.success) {
|
||||||
|
orderList.value = res.result.records;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function getCollectList () { // 获取收藏列表
|
||||||
|
const params = {
|
||||||
|
pageNumber: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
type: 'GOODS'
|
||||||
|
}
|
||||||
|
loading.value = true
|
||||||
|
fetchCollectList(params).then(res => {
|
||||||
|
loading.value = false
|
||||||
|
collectList.value = res.result.records
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function cancelCollect (id) { // 取消商品收藏
|
||||||
|
apiCancelCollect('GOODS', id).then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
Message.success('取消收藏成功')
|
||||||
|
getCollectList();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function getTracksList () { // 获取足迹列表
|
||||||
|
const params = {
|
||||||
|
pageNumber: 1,
|
||||||
|
pageSize: 20
|
||||||
|
}
|
||||||
|
loading.value = true
|
||||||
|
fetchTracksList(params).then(res => {
|
||||||
|
tracksList.value = res.result.records
|
||||||
|
loading.value = false
|
||||||
|
}).catch(() => { loading.value = false })
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -20,82 +20,82 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { computed, ref } from 'vue'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { Modal } from "@/utils/message";
|
import { Modal } from "@/utils/message";
|
||||||
import Storage from '@/plugins/storage.js';
|
import storage from '@/plugins/storage.js';
|
||||||
import Configuration from './config';
|
import Configuration from './config';
|
||||||
import drawerPage from './Drawer'
|
import drawerPage from './Drawer'
|
||||||
import {cartCount} from '@/api/cart.js'
|
import {cartCount} from '@/api/cart.js'
|
||||||
import { resolveIcon } from '@/utils/iconMap'
|
import { resolveIcon } from '@/utils/iconMap'
|
||||||
export default {
|
import { useAppStore } from '@/stores/app'
|
||||||
name: 'Main',
|
|
||||||
data () {
|
defineOptions({ name: 'Main' })
|
||||||
return {
|
|
||||||
resetConfig: Configuration, // 菜单项
|
const route = useRoute()
|
||||||
handleDrawer: false, // 是否可展开
|
const router = useRouter()
|
||||||
drawerData: '' // 菜单基础数据
|
const appStore = useAppStore()
|
||||||
}
|
|
||||||
},
|
const resetConfig = ref(Configuration) // 菜单项
|
||||||
components: {drawerPage},
|
const handleDrawer = ref(false) // 是否可展开
|
||||||
computed: {
|
const drawerData = ref('') // 菜单基础数据
|
||||||
// 用户信息
|
|
||||||
userInfo () {
|
// 用户信息
|
||||||
return Storage.getItem('userInfo');
|
const userInfo = computed(() => {
|
||||||
},
|
return storage.getItem('userInfo');
|
||||||
// 购物车商品数量
|
})
|
||||||
cartNum () {
|
|
||||||
return this.$store.state.cartNum
|
// 购物车商品数量
|
||||||
}
|
const cartNum = computed(() => appStore.cartNum)
|
||||||
},
|
|
||||||
methods: {
|
function showCartNum (item) { // 获取购物车数量
|
||||||
resolveIcon,
|
if (userInfo.value && item.title === '购物车') {
|
||||||
showCartNum (item) { // 获取购物车数量
|
getCartList()
|
||||||
if (this.userInfo && item.title === '购物车') {
|
}
|
||||||
this.getCartList()
|
}
|
||||||
}
|
|
||||||
},
|
function clickBar (val) { // tabbar点击操作
|
||||||
clickBar (val) { // tabbar点击操作
|
if (!userInfo.value) {
|
||||||
if (!this.userInfo) {
|
Modal.confirm({
|
||||||
Modal.confirm({
|
title: '请登录',
|
||||||
title: '请登录',
|
content: '请登录后执行此操作',
|
||||||
content: '请登录后执行此操作',
|
okText: '立即登录',
|
||||||
okText: '立即登录',
|
cancelText: '继续浏览',
|
||||||
cancelText: '继续浏览',
|
onOk: () => {
|
||||||
onOk: () => {
|
router.push({
|
||||||
this.$router.push({
|
path: '/login',
|
||||||
path: '/login',
|
query: {
|
||||||
query: {
|
rePath: route.path,
|
||||||
rePath: this.$route.path,
|
query: JSON.stringify(route.query || {}),
|
||||||
query: JSON.stringify(this.$route.query || {}),
|
},
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
if (val.display) {
|
|
||||||
this.handleDrawer = true
|
|
||||||
this.drawerData = val
|
|
||||||
} else {
|
|
||||||
this.handleDrawer = false
|
|
||||||
switch (val.title) {
|
|
||||||
case '会员中心':
|
|
||||||
this.$router.push('/home')
|
|
||||||
break;
|
|
||||||
case '我的资产':
|
|
||||||
this.$router.push('/home/MoneyManagement')
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
getCartList () { // 获取购物车列表
|
} else {
|
||||||
cartCount().then(res => {
|
if (val.display) {
|
||||||
this.$store.commit('SET_CARTNUM', res.result)
|
handleDrawer.value = true
|
||||||
this.Cookies.setItem('cartNum', res.result)
|
drawerData.value = val
|
||||||
})
|
} else {
|
||||||
|
handleDrawer.value = false
|
||||||
|
switch (val.title) {
|
||||||
|
case '会员中心':
|
||||||
|
router.push('/home')
|
||||||
|
break;
|
||||||
|
case '我的资产':
|
||||||
|
router.push('/home/MoneyManagement')
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getCartList () { // 获取购物车列表
|
||||||
|
cartCount().then(res => {
|
||||||
|
appStore.setCartNum(res.result)
|
||||||
|
storage.setItem('cartNum', res.result)
|
||||||
|
})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -11,20 +11,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
defineOptions({ name: 'Main' })
|
||||||
name: 'Main',
|
|
||||||
props: {
|
defineProps({
|
||||||
_Title: { // 描述内容
|
_Title: { // 描述内容
|
||||||
type: null,
|
type: null,
|
||||||
default: '暂无更多'
|
default: '暂无更多'
|
||||||
},
|
},
|
||||||
_Size: { // 图片大小
|
_Size: { // 图片大小
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 150
|
default: 150
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -8,107 +8,105 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { onMounted, ref } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { getIconUrl } from "@/assets/iconfont/iconMap";
|
import { getIconUrl } from "@/assets/iconfont/iconMap";
|
||||||
import storage from "@/plugins/storage.js";
|
import storage from "@/plugins/storage.js";
|
||||||
|
|
||||||
export default {
|
defineOptions({ name: "fixed-index" });
|
||||||
name: "fixed-index",
|
|
||||||
data(){
|
const route = useRoute();
|
||||||
return{
|
const router = useRouter();
|
||||||
iconList:[
|
|
||||||
{
|
const iconList = ref([
|
||||||
icon:"user",
|
{
|
||||||
label:"会员中心",
|
icon:"user",
|
||||||
path:"/home",
|
label:"会员中心",
|
||||||
requireLogin: true,
|
path:"/home",
|
||||||
},
|
requireLogin: true,
|
||||||
{
|
},
|
||||||
icon:"carts",
|
{
|
||||||
label:"购物车",
|
icon:"carts",
|
||||||
path:"/cart",
|
label:"购物车",
|
||||||
requireLogin: true,
|
path:"/cart",
|
||||||
},
|
requireLogin: true,
|
||||||
{
|
},
|
||||||
icon:"notification",
|
{
|
||||||
label:"消息",
|
icon:"notification",
|
||||||
path:"/home/MsgList",
|
label:"消息",
|
||||||
requireLogin: true,
|
path:"/home/MsgList",
|
||||||
},
|
requireLogin: true,
|
||||||
{
|
},
|
||||||
icon:"collage",
|
{
|
||||||
label:"收藏",
|
icon:"collage",
|
||||||
path:"/home/Favorites",
|
label:"收藏",
|
||||||
requireLogin: true,
|
path:"/home/Favorites",
|
||||||
},
|
requireLogin: true,
|
||||||
{
|
},
|
||||||
icon:"back",
|
{
|
||||||
label:"顶部",
|
icon:"back",
|
||||||
path:"back"
|
label:"顶部",
|
||||||
},
|
path:"back"
|
||||||
],
|
},
|
||||||
isScrolling:false,
|
]);
|
||||||
timeoutId:"",
|
let isScrolling = false;
|
||||||
|
let timeoutId = "";
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 添加滚轮事件监听器
|
||||||
|
window.addEventListener('wheel', function() {
|
||||||
|
if (isScrolling) {
|
||||||
|
// 如果页面正在滚动,则取消定时器并设置 isScrolling 为 false
|
||||||
|
window.cancelAnimationFrame(timeoutId);
|
||||||
|
isScrolling = false;
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
mounted() {
|
|
||||||
const vm = this
|
|
||||||
// 添加滚轮事件监听器
|
|
||||||
window.addEventListener('wheel', function() {
|
|
||||||
if (vm.isScrolling) {
|
|
||||||
// 如果页面正在滚动,则取消定时器并设置 isScrolling 为 false
|
|
||||||
window.cancelAnimationFrame(vm.timeoutId);
|
|
||||||
vm.isScrolling = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
},
|
});
|
||||||
methods:{
|
|
||||||
getIconUrl,
|
function scrollToTop() {
|
||||||
scrollToTop() {
|
const c = document.documentElement.scrollTop || document.body.scrollTop;
|
||||||
const c = document.documentElement.scrollTop || document.body.scrollTop;
|
if (c > 0) {
|
||||||
if (c > 0) {
|
timeoutId = window.requestAnimationFrame(scrollToTop);
|
||||||
this.timeoutId = window.requestAnimationFrame(this.scrollToTop);
|
window.scrollTo(0, c - c / 4);
|
||||||
window.scrollTo(0, c - c / 4);
|
isScrolling = true;
|
||||||
this.isScrolling = true;
|
}else {
|
||||||
}else {
|
isScrolling = false;
|
||||||
this.isScrolling = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
isLoggedIn () {
|
|
||||||
const rawUserInfo = storage.getItem('userInfo');
|
|
||||||
if (rawUserInfo) {
|
|
||||||
try {
|
|
||||||
return !!JSON.parse(rawUserInfo).username;
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return !!storage.getItem('accessToken');
|
|
||||||
},
|
|
||||||
goLogin (rePath) {
|
|
||||||
this.$router.push({
|
|
||||||
path: '/login',
|
|
||||||
query: {
|
|
||||||
rePath,
|
|
||||||
query: JSON.stringify(this.$route.query || {}),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
handleClickIcon (val) {
|
|
||||||
if (val.path === 'back') {
|
|
||||||
this.scrollToTop();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (val.requireLogin && !this.isLoggedIn()) {
|
|
||||||
this.goLogin(val.path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.$router.push(val.path);
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function isLoggedIn () {
|
||||||
|
const rawUserInfo = storage.getItem('userInfo');
|
||||||
|
if (rawUserInfo) {
|
||||||
|
try {
|
||||||
|
return !!JSON.parse(rawUserInfo).username;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !!storage.getItem('accessToken');
|
||||||
|
}
|
||||||
|
function goLogin (rePath) {
|
||||||
|
router.push({
|
||||||
|
path: '/login',
|
||||||
|
query: {
|
||||||
|
rePath,
|
||||||
|
query: JSON.stringify(route.query || {}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function handleClickIcon (val) {
|
||||||
|
if (val.path === 'back') {
|
||||||
|
scrollToTop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (val.requireLogin && !isLoggedIn()) {
|
||||||
|
goLogin(val.path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
router.push(val.path);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -68,32 +68,29 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import config from '@/config';
|
import { ref } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import config from '@/config'
|
||||||
|
|
||||||
export default {
|
defineOptions({ name: 'Footer' })
|
||||||
name: "Footer",
|
|
||||||
data() {
|
const router = useRouter()
|
||||||
return {
|
|
||||||
config,
|
const guideArr = ref([
|
||||||
guideArr: [
|
// 导航链接
|
||||||
// 导航链接
|
["购物指南", "购物流程", "会员介绍", "生活旅行", "常见问题"],
|
||||||
["购物指南", "购物流程", "会员介绍", "生活旅行", "常见问题"],
|
["配送方式", "上门自提", "配送查询", "收取标准", "物流规则"],
|
||||||
["配送方式", "上门自提", "配送查询", "收取标准", "物流规则"],
|
["支付方式", "在线支付", "公司转账", "余额支付", "积分支付"],
|
||||||
["支付方式", "在线支付", "公司转账", "余额支付", "积分支付"],
|
["售后服务", "售后政策", "退款说明", "返修/退货", "取消订单"],
|
||||||
["售后服务", "售后政策", "退款说明", "返修/退货", "取消订单"],
|
])
|
||||||
],
|
const moreLink = ref(["关于我们", "联系我们", "联系客服", "商家帮助", "隐私政策"]) // 更多链接
|
||||||
moreLink: ["关于我们", "联系我们", "联系客服", "商家帮助", "隐私政策"], // 更多链接
|
const year = ref(new Date().getFullYear()) // 当前年份
|
||||||
year: new Date().getFullYear(), // 当前年份
|
|
||||||
};
|
function goArticle() {
|
||||||
},
|
// 跳转文章页
|
||||||
methods: {
|
router.push("/article")
|
||||||
goArticle() {
|
}
|
||||||
// 跳转文章页
|
|
||||||
this.$router.push("/article");
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -1,78 +1,82 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="goods-thumb" :class="{ 'is-loaded': loaded, 'is-error': error }">
|
<div class="goods-thumb" ref="rootEl" :class="{ 'is-loaded': loaded, 'is-error': error }">
|
||||||
<img v-if="currentSrc" :src="currentSrc" :alt="alt" @load="loaded = true" @error="onError" />
|
<img v-if="currentSrc" :src="currentSrc" :alt="alt" @load="loaded = true" @error="onError" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
import { onBeforeUnmount, onMounted, ref, watch } from "vue";
|
||||||
name: "GoodsThumb",
|
|
||||||
props: {
|
defineOptions({ name: "GoodsThumb" });
|
||||||
src: {
|
|
||||||
type: String,
|
const props = defineProps({
|
||||||
default: "",
|
src: {
|
||||||
},
|
type: String,
|
||||||
alt: {
|
default: "",
|
||||||
type: String,
|
|
||||||
default: "",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
data() {
|
alt: {
|
||||||
return {
|
type: String,
|
||||||
currentSrc: "",
|
default: "",
|
||||||
loaded: false,
|
|
||||||
error: false,
|
|
||||||
observer: null,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
watch: {
|
});
|
||||||
src() {
|
|
||||||
this.observe();
|
const rootEl = ref(null);
|
||||||
|
const currentSrc = ref("");
|
||||||
|
const loaded = ref(false);
|
||||||
|
const error = ref(false);
|
||||||
|
let observer = null;
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.src,
|
||||||
|
() => {
|
||||||
|
observe();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
function disconnect() {
|
||||||
|
if (observer) {
|
||||||
|
observer.disconnect();
|
||||||
|
observer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function observe() {
|
||||||
|
disconnect();
|
||||||
|
currentSrc.value = "";
|
||||||
|
loaded.value = false;
|
||||||
|
error.value = false;
|
||||||
|
if (!props.src) {
|
||||||
|
error.value = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (typeof IntersectionObserver === "undefined") {
|
||||||
|
currentSrc.value = props.src;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
observer = new IntersectionObserver(
|
||||||
|
(entries) => {
|
||||||
|
if (!entries.some((entry) => entry.isIntersecting)) return;
|
||||||
|
currentSrc.value = props.src;
|
||||||
|
disconnect();
|
||||||
},
|
},
|
||||||
},
|
{ rootMargin: "160px 0px" }
|
||||||
mounted() {
|
);
|
||||||
this.observe();
|
observer.observe(rootEl.value);
|
||||||
},
|
}
|
||||||
beforeUnmount() {
|
|
||||||
this.disconnect();
|
function onError() {
|
||||||
},
|
error.value = true;
|
||||||
methods: {
|
loaded.value = false;
|
||||||
disconnect() {
|
currentSrc.value = "";
|
||||||
if (this.observer) {
|
}
|
||||||
this.observer.disconnect();
|
|
||||||
this.observer = null;
|
onMounted(() => {
|
||||||
}
|
observe();
|
||||||
},
|
});
|
||||||
observe() {
|
|
||||||
this.disconnect();
|
onBeforeUnmount(() => {
|
||||||
this.currentSrc = "";
|
disconnect();
|
||||||
this.loaded = false;
|
});
|
||||||
this.error = false;
|
|
||||||
if (!this.src) {
|
|
||||||
this.error = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (typeof IntersectionObserver === "undefined") {
|
|
||||||
this.currentSrc = this.src;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.observer = new IntersectionObserver(
|
|
||||||
(entries) => {
|
|
||||||
if (!entries.some((entry) => entry.isIntersecting)) return;
|
|
||||||
this.currentSrc = this.src;
|
|
||||||
this.disconnect();
|
|
||||||
},
|
|
||||||
{ rootMargin: "160px 0px" }
|
|
||||||
);
|
|
||||||
this.observer.observe(this.$el);
|
|
||||||
},
|
|
||||||
onError() {
|
|
||||||
this.error = true;
|
|
||||||
this.loaded = false;
|
|
||||||
this.currentSrc = "";
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -18,60 +18,57 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
import { AlarmClock } from '@element-plus/icons-vue';
|
import { AlarmClock } from '@element-plus/icons-vue';
|
||||||
export default {
|
|
||||||
components: { AlarmClock },
|
const props = defineProps({
|
||||||
props: {
|
time: { // 传入的初始时间
|
||||||
time: { // 传入的初始时间
|
default: 1718977559428
|
||||||
default: 1718977559428
|
|
||||||
},
|
|
||||||
type: { // 区分是在详情还是购物车调用
|
|
||||||
default: 'goodsDetail', // 设置两个值,goodsDetail和cart,样式不同
|
|
||||||
type: String
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
data () {
|
type: { // 区分是在详情还是购物车调用
|
||||||
return {
|
default: 'goodsDetail', // 设置两个值,goodsDetail和cart,样式不同
|
||||||
end: '', // 结束状态
|
type: String
|
||||||
hours: '', // 小时
|
|
||||||
minutes: '', // 分钟
|
|
||||||
seconds: '', // 秒
|
|
||||||
interval: '' // 定时器
|
|
||||||
};
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
this.init()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
countDown (val) { // 倒计时方法
|
|
||||||
function addZero (i) {
|
|
||||||
return i < 10 ? '0' + i : i + '';
|
|
||||||
}
|
|
||||||
var nowtime = new Date();
|
|
||||||
var endtime = new Date(val);
|
|
||||||
var lefttime = parseInt((endtime.getTime() - nowtime.getTime()) / 1000);
|
|
||||||
var h = parseInt((lefttime / (60 * 60)) % 24);
|
|
||||||
var m = parseInt((lefttime / 60) % 60);
|
|
||||||
var s = parseInt(lefttime % 60);
|
|
||||||
h = addZero(h);
|
|
||||||
m = addZero(m);
|
|
||||||
s = addZero(s);
|
|
||||||
this.hours = h;
|
|
||||||
this.minutes = m
|
|
||||||
this.seconds = s;
|
|
||||||
if (lefttime <= 0) {
|
|
||||||
this.end = `活动已结束`;
|
|
||||||
clearInterval(this.interval)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
init () { // 初始化
|
|
||||||
this.interval = setInterval(() => {
|
|
||||||
this.countDown(this.time);
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
|
const end = ref(''); // 结束状态
|
||||||
|
const hours = ref(''); // 小时
|
||||||
|
const minutes = ref(''); // 分钟
|
||||||
|
const seconds = ref(''); // 秒
|
||||||
|
let interval = null; // 定时器
|
||||||
|
|
||||||
|
function countDown (val) { // 倒计时方法
|
||||||
|
function addZero (i) {
|
||||||
|
return i < 10 ? '0' + i : i + '';
|
||||||
|
}
|
||||||
|
var nowtime = new Date();
|
||||||
|
var endtime = new Date(val);
|
||||||
|
var lefttime = parseInt((endtime.getTime() - nowtime.getTime()) / 1000);
|
||||||
|
var h = parseInt((lefttime / (60 * 60)) % 24);
|
||||||
|
var m = parseInt((lefttime / 60) % 60);
|
||||||
|
var s = parseInt(lefttime % 60);
|
||||||
|
h = addZero(h);
|
||||||
|
m = addZero(m);
|
||||||
|
s = addZero(s);
|
||||||
|
hours.value = h;
|
||||||
|
minutes.value = m
|
||||||
|
seconds.value = s;
|
||||||
|
if (lefttime <= 0) {
|
||||||
|
end.value = `活动已结束`;
|
||||||
|
clearInterval(interval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init () { // 初始化
|
||||||
|
interval = setInterval(() => {
|
||||||
|
countDown(props.time);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
init()
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.cart-promotion{
|
.cart-promotion{
|
||||||
|
|||||||
@@ -108,7 +108,7 @@
|
|||||||
class="item-coupon"
|
class="item-coupon"
|
||||||
v-for="(item, index) in promotionMap['COUPON'].slice(0, 6)"
|
v-for="(item, index) in promotionMap['COUPON'].slice(0, 6)"
|
||||||
:key="index"
|
:key="index"
|
||||||
@click="receiveCoupon(item.id)"
|
@click="handleReceiveCoupon(item.id)"
|
||||||
>
|
>
|
||||||
<span v-if="item.couponType == 'PRICE'"
|
<span v-if="item.couponType == 'PRICE'"
|
||||||
>满{{ item.consumeThreshold }}减{{ item.price }}</span>
|
>满{{ item.consumeThreshold }}减{{ item.price }}</span>
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
class="item-coupon"
|
class="item-coupon"
|
||||||
v-for="(item, index) in promotionMap['COUPON'].slice(6, promotionMap['COUPON'].length)"
|
v-for="(item, index) in promotionMap['COUPON'].slice(6, promotionMap['COUPON'].length)"
|
||||||
:key="index"
|
:key="index"
|
||||||
@click="receiveCoupon(item.id)"
|
@click="handleReceiveCoupon(item.id)"
|
||||||
>
|
>
|
||||||
<span v-if="item.couponType == 'PRICE'"
|
<span v-if="item.couponType == 'PRICE'"
|
||||||
>满{{ item.consumeThreshold }}减{{ item.price }}</span>
|
>满{{ item.consumeThreshold }}减{{ item.price }}</span>
|
||||||
@@ -318,11 +318,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { ref, computed, watch, onMounted } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
import { StarFilled } from '@element-plus/icons-vue';
|
import { StarFilled } from '@element-plus/icons-vue';
|
||||||
import Promotion from "./Promotion.vue";
|
import Promotion from "./Promotion.vue";
|
||||||
import DPlayer from "dplayer";
|
import DPlayer from "dplayer";
|
||||||
|
import storage from "@/plugins/storage.js";
|
||||||
import {
|
import {
|
||||||
collectGoods,
|
collectGoods,
|
||||||
isCollection,
|
isCollection,
|
||||||
@@ -344,375 +347,389 @@ import {
|
|||||||
E_COUPON_CART_POINTS,
|
E_COUPON_CART_POINTS,
|
||||||
} from "@/constants/goodsType";
|
} from "@/constants/goodsType";
|
||||||
|
|
||||||
export default {
|
defineOptions({ name: "ShowGoods" });
|
||||||
name: "ShowGoods",
|
|
||||||
props: {
|
const props = defineProps({
|
||||||
// 商品数据
|
// 商品数据
|
||||||
detail: {
|
detail: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
watch: {
|
});
|
||||||
detail: {
|
|
||||||
handler(val) {
|
const emit = defineEmits(["handleClickSku"]);
|
||||||
this.skuDetail = val.data;
|
|
||||||
this.wholesaleList = val.wholesaleList;
|
const route = useRoute();
|
||||||
this.syncCountWithQuantity();
|
const router = useRouter();
|
||||||
this.promotion();
|
|
||||||
this.swiperGoodsImg();
|
const showGoodsVideo = ref(false);
|
||||||
},
|
const goodsVideo = ref("");
|
||||||
deep: true,
|
const wholesaleList = ref([]);
|
||||||
immediate: true,
|
const count = ref(1); // 商品数量
|
||||||
},
|
const imgIndex = ref(0); // 展示图片下标
|
||||||
showGoodsVideo(val){
|
const currentSelceted = ref([]); // 当前商品sku
|
||||||
if(val){
|
const imgList = ref([]); // 商品图片列表
|
||||||
this.initVideo();
|
const skuDetail = ref({
|
||||||
|
specList: [],
|
||||||
|
}); // sku详情
|
||||||
|
const goodsSpecList = ref(props.detail.specs); // 商品spec
|
||||||
|
const promotionMap = ref(createEmptyPromotionMap());
|
||||||
|
const formatList = ref([]); // 选择商品品类的数组
|
||||||
|
const loading = ref(false); // 立即购买loading
|
||||||
|
const loading1 = ref(false); // 加入购物车loading
|
||||||
|
const loadingPintuan = ref(false);
|
||||||
|
const isCollected = ref(false); // 是否收藏
|
||||||
|
const skuList = ref([]); // formatSku 中赋值(原 data 未声明)
|
||||||
|
|
||||||
|
// startCheckout 通过字符串 key 操作对应 loading
|
||||||
|
const loadings = { loading, loading1, loadingPintuan };
|
||||||
|
|
||||||
|
const currentImg = computed(() => {
|
||||||
|
const item = imgList.value[imgIndex.value];
|
||||||
|
return item ? item.url || item : "";
|
||||||
|
});
|
||||||
|
|
||||||
|
const wholesalePrice = computed(() => {
|
||||||
|
return wholesaleList.value.length
|
||||||
|
? wholesaleList.value.map((item) => {
|
||||||
|
return item.price;
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
});
|
||||||
|
|
||||||
|
const wholesaleNum = computed(() => {
|
||||||
|
return wholesaleList.value.length
|
||||||
|
? wholesaleList.value.map((item) => {
|
||||||
|
return item.num;
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 卡密商品:禁加购,仅立即购买;库存读 quantity(M-02 起支持促销) */
|
||||||
|
const isECouponGoods = computed(() => {
|
||||||
|
return isECoupon(skuDetail.value.goodsType);
|
||||||
|
});
|
||||||
|
|
||||||
|
const displayStock = computed(() => {
|
||||||
|
if (isECouponGoods.value) {
|
||||||
|
return getECouponStock(skuDetail.value);
|
||||||
|
}
|
||||||
|
return Number(skuDetail.value.quantity) || 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
const quantityMax = computed(() => {
|
||||||
|
if (isECouponGoods.value) {
|
||||||
|
return getECouponMaxBuyNum(skuDetail.value);
|
||||||
|
}
|
||||||
|
const qty = Number(skuDetail.value.quantity) || 0;
|
||||||
|
return qty > 0 ? qty : 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
const isOutOfStock = computed(() => {
|
||||||
|
if (isECouponGoods.value) {
|
||||||
|
return getECouponStock(skuDetail.value) <= 0;
|
||||||
|
}
|
||||||
|
return (Number(skuDetail.value.quantity) || 0) <= 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.detail,
|
||||||
|
(val) => {
|
||||||
|
skuDetail.value = val.data;
|
||||||
|
wholesaleList.value = val.wholesaleList;
|
||||||
|
syncCountWithQuantity();
|
||||||
|
promotion();
|
||||||
|
swiperGoodsImg();
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(showGoodsVideo, (val) => {
|
||||||
|
if (val) {
|
||||||
|
initVideo();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 初始化video
|
||||||
|
function initVideo() {
|
||||||
|
if (!goodsVideo.value) {
|
||||||
|
setTimeout(() => {
|
||||||
|
goodsVideo.value = new DPlayer({
|
||||||
|
container: document.getElementById('dplayer'),
|
||||||
|
video: {
|
||||||
|
url: skuDetail.value.goodsVideo,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncCountWithQuantity() {
|
||||||
|
const qty = isECouponGoods.value
|
||||||
|
? getECouponStock(skuDetail.value)
|
||||||
|
: Number(skuDetail.value.quantity) || 0;
|
||||||
|
if (qty <= 0) {
|
||||||
|
count.value = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!isECouponGoods.value && wholesaleList.value && wholesaleList.value.length > 0) {
|
||||||
|
count.value = Math.min(Math.max(wholesaleList.value[0].num, 1), qty);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const max = isECouponGoods.value ? getECouponMaxBuyNum(skuDetail.value) : qty;
|
||||||
|
if (count.value > max) {
|
||||||
|
count.value = max;
|
||||||
|
} else if (count.value < 1) {
|
||||||
|
count.value = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeCount(val) {
|
||||||
|
if (wholesaleList.value && wholesaleList.value.length > 0) {
|
||||||
|
if (count.value <= wholesaleList.value[0].num) {
|
||||||
|
Message.warning("批发商品购买数量不能小于起批数量");
|
||||||
|
count.value = wholesaleList.value[0].num;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function select(index, value) {
|
||||||
|
// 选择规格
|
||||||
|
currentSelceted.value[index] = value;
|
||||||
|
let selectedSkuId = goodsSpecList.value.find((i) => {
|
||||||
|
let matched = true;
|
||||||
|
let specValues = i.specValues.filter((j) => j.specName !== "images");
|
||||||
|
for (let n = 0; n < specValues.length; n++) {
|
||||||
|
if (specValues[n].specValue !== currentSelceted.value[n]) {
|
||||||
|
matched = false;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
if (matched) {
|
||||||
data() {
|
return i;
|
||||||
return {
|
}
|
||||||
playIcon,
|
});
|
||||||
showGoodsVideo:false,
|
emit("handleClickSku", {
|
||||||
goodsVideo:"",
|
skuId: selectedSkuId.skuId,
|
||||||
wholesaleList: [],
|
goodsId: skuDetail.value.goodsId,
|
||||||
count: 1, // 商品数量
|
});
|
||||||
imgIndex: 0, // 展示图片下标
|
}
|
||||||
currentSelceted: [], // 当前商品sku
|
|
||||||
imgList: [], // 商品图片列表
|
|
||||||
skuDetail: {
|
|
||||||
specList: [],
|
|
||||||
}, // sku详情
|
|
||||||
goodsSpecList: this.detail.specs, // 商品spec
|
|
||||||
promotionMap: createEmptyPromotionMap(),
|
|
||||||
formatList: [], // 选择商品品类的数组
|
|
||||||
loading: false, // 立即购买loading
|
|
||||||
loading1: false, // 加入购物车loading
|
|
||||||
loadingPintuan: false,
|
|
||||||
isCollected: false, // 是否收藏
|
|
||||||
};
|
|
||||||
},
|
|
||||||
components: { Promotion, StarFilled },
|
|
||||||
computed: {
|
|
||||||
currentImg() {
|
|
||||||
const item = this.imgList[this.imgIndex];
|
|
||||||
return item ? item.url || item : "";
|
|
||||||
},
|
|
||||||
wholesalePrice(key) {
|
|
||||||
return this.wholesaleList.length
|
|
||||||
? this.wholesaleList.map((item) => {
|
|
||||||
return item.price;
|
|
||||||
})
|
|
||||||
: [];
|
|
||||||
},
|
|
||||||
wholesaleNum(key) {
|
|
||||||
return this.wholesaleList.length
|
|
||||||
? this.wholesaleList.map((item) => {
|
|
||||||
return item.num;
|
|
||||||
})
|
|
||||||
: [];
|
|
||||||
},
|
|
||||||
/** 卡密商品:禁加购,仅立即购买;库存读 quantity(M-02 起支持促销) */
|
|
||||||
isECouponGoods() {
|
|
||||||
return isECoupon(this.skuDetail.goodsType);
|
|
||||||
},
|
|
||||||
displayStock() {
|
|
||||||
if (this.isECouponGoods) {
|
|
||||||
return getECouponStock(this.skuDetail);
|
|
||||||
}
|
|
||||||
return Number(this.skuDetail.quantity) || 0;
|
|
||||||
},
|
|
||||||
quantityMax() {
|
|
||||||
if (this.isECouponGoods) {
|
|
||||||
return getECouponMaxBuyNum(this.skuDetail);
|
|
||||||
}
|
|
||||||
const qty = Number(this.skuDetail.quantity) || 0;
|
|
||||||
return qty > 0 ? qty : 1;
|
|
||||||
},
|
|
||||||
isOutOfStock() {
|
|
||||||
if (this.isECouponGoods) {
|
|
||||||
return getECouponStock(this.skuDetail) <= 0;
|
|
||||||
}
|
|
||||||
return (Number(this.skuDetail.quantity) || 0) <= 0;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
eCouponAfterSaleHint,
|
|
||||||
// 初始化video
|
|
||||||
initVideo(){
|
|
||||||
if(!this.goodsVideo ){
|
|
||||||
setTimeout(()=>{
|
|
||||||
this.goodsVideo = new DPlayer({
|
|
||||||
container: document.getElementById('dplayer'),
|
|
||||||
video: {
|
|
||||||
url:this.skuDetail.goodsVideo,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},100)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
function addShoppingCartBtn() {
|
||||||
|
// FR-B-01:E_COUPON 禁止加购物车(后端亦返回 CARD_KEY_E_COUPON_CART_FORBIDDEN)
|
||||||
},
|
if (isECouponGoods.value) {
|
||||||
|
Message.warning("电子卡券请使用立即购买");
|
||||||
|
return;
|
||||||
syncCountWithQuantity() {
|
}
|
||||||
const qty = this.isECouponGoods
|
// 添加购物车
|
||||||
? getECouponStock(this.skuDetail)
|
const params = {
|
||||||
: Number(this.skuDetail.quantity) || 0;
|
num: count.value,
|
||||||
if (qty <= 0) {
|
skuId: skuDetail.value.id,
|
||||||
this.count = 1;
|
};
|
||||||
return;
|
loading.value = true;
|
||||||
}
|
addCartGoods(params)
|
||||||
if (!this.isECouponGoods && this.wholesaleList && this.wholesaleList.length > 0) {
|
.then((res) => {
|
||||||
this.count = Math.min(Math.max(this.wholesaleList[0].num, 1), qty);
|
loading.value = false;
|
||||||
return;
|
if (res.success) {
|
||||||
}
|
router.push({
|
||||||
const max = this.isECouponGoods ? getECouponMaxBuyNum(this.skuDetail) : qty;
|
path: "/shoppingCart",
|
||||||
if (this.count > max) {
|
|
||||||
this.count = max;
|
|
||||||
} else if (this.count < 1) {
|
|
||||||
this.count = 1;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
changeCount(val) {
|
|
||||||
if (this.wholesaleList && this.wholesaleList.length > 0) {
|
|
||||||
if (this.count <= this.wholesaleList[0].num) {
|
|
||||||
Message.warning("批发商品购买数量不能小于起批数量");
|
|
||||||
this.count = this.wholesaleList[0].num;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
select(index, value) {
|
|
||||||
// 选择规格
|
|
||||||
this.currentSelceted[index] = value;
|
|
||||||
let selectedSkuId = this.goodsSpecList.find((i) => {
|
|
||||||
let matched = true;
|
|
||||||
let specValues = i.specValues.filter((j) => j.specName !== "images");
|
|
||||||
for (let n = 0; n < specValues.length; n++) {
|
|
||||||
if (specValues[n].specValue !== this.currentSelceted[n]) {
|
|
||||||
matched = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (matched) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.$emit("handleClickSku", {
|
|
||||||
skuId: selectedSkuId.skuId,
|
|
||||||
goodsId: this.skuDetail.goodsId,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
addShoppingCartBtn() {
|
|
||||||
// FR-B-01:E_COUPON 禁止加购物车(后端亦返回 CARD_KEY_E_COUPON_CART_FORBIDDEN)
|
|
||||||
if (this.isECouponGoods) {
|
|
||||||
Message.warning("电子卡券请使用立即购买");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 添加购物车
|
|
||||||
const params = {
|
|
||||||
num: this.count,
|
|
||||||
skuId: this.skuDetail.id,
|
|
||||||
};
|
|
||||||
this.loading = true;
|
|
||||||
addCartGoods(params)
|
|
||||||
.then((res) => {
|
|
||||||
this.loading = false;
|
|
||||||
if (res.success) {
|
|
||||||
this.$router.push({
|
|
||||||
path: "/shoppingCart",
|
|
||||||
query: {
|
|
||||||
skuId: this.skuDetail.id,
|
|
||||||
goodsId: this.skuDetail.goodsId,
|
|
||||||
thumbnail: this.skuDetail.thumbnail,
|
|
||||||
goodsName: this.skuDetail.goodsName,
|
|
||||||
count: this.count,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
Message.warning(res.message);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
this.loading = false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
buyNow() {
|
|
||||||
this.startCheckout(E_COUPON_CART_BUY_NOW, "loading1");
|
|
||||||
},
|
|
||||||
pintuanBuy() {
|
|
||||||
this.startCheckout(E_COUPON_CART_PINTUAN, "loadingPintuan");
|
|
||||||
},
|
|
||||||
pointPay() {
|
|
||||||
this.startCheckout(E_COUPON_CART_POINTS, "loading");
|
|
||||||
},
|
|
||||||
startCheckout(action, loadingKey) {
|
|
||||||
const params = {
|
|
||||||
num: this.count,
|
|
||||||
skuId: this.skuDetail.id,
|
|
||||||
cartType: "BUY_NOW",
|
|
||||||
};
|
|
||||||
if (this.skuDetail.goodsType === "VIRTUAL_GOODS") {
|
|
||||||
params.cartType = "VIRTUAL";
|
|
||||||
} else if (this.isECouponGoods) {
|
|
||||||
params.cartType = resolveECouponCartType({
|
|
||||||
routeWay: this.$route.query.way,
|
|
||||||
action,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this[loadingKey] = true;
|
|
||||||
addCartGoods(params)
|
|
||||||
.then((res) => {
|
|
||||||
this[loadingKey] = false;
|
|
||||||
if (res.success) {
|
|
||||||
this.$router.push({
|
|
||||||
path: "/pay",
|
|
||||||
query: { way: params.cartType },
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
Message.warning(res.message);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
this[loadingKey] = false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
async collect() {
|
|
||||||
if (!this.Cookies.getItem("accessToken") || !this.Cookies.getItem("userInfo")) {
|
|
||||||
this.$router.push({
|
|
||||||
path: "/login",
|
|
||||||
query: {
|
query: {
|
||||||
rePath: this.$route.path,
|
skuId: skuDetail.value.id,
|
||||||
query: JSON.stringify(this.$route.query || {}),
|
goodsId: skuDetail.value.goodsId,
|
||||||
|
thumbnail: skuDetail.value.thumbnail,
|
||||||
|
goodsName: skuDetail.value.goodsName,
|
||||||
|
count: count.value,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 收藏商品
|
|
||||||
if (this.isCollected) {
|
|
||||||
let cancel = await cancelCollect("GOODS", this.skuDetail.id);
|
|
||||||
if (cancel.success) {
|
|
||||||
Message.success("取消收藏成功");
|
|
||||||
this.isCollected = false;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
let collect = await collectGoods("GOODS", this.skuDetail.id);
|
Message.warning(res.message);
|
||||||
if (collect.code === 200) {
|
|
||||||
this.isCollected = true;
|
|
||||||
Message.success("收藏商品成功,可以前往个人中心我的收藏查看");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
// 格式化数据
|
.catch(() => {
|
||||||
formatSku(list) {
|
loading.value = false;
|
||||||
let arr = [{}];
|
});
|
||||||
list.forEach((item, index) => {
|
}
|
||||||
item.specValues.forEach((spec, specIndex) => {
|
|
||||||
let name = spec.specName;
|
|
||||||
let values = {
|
|
||||||
value: spec.specValue,
|
|
||||||
quantity: item.quantity,
|
|
||||||
};
|
|
||||||
if (name === "images") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
arr.forEach((arrItem, arrIndex) => {
|
function buyNow() {
|
||||||
if (
|
startCheckout(E_COUPON_CART_BUY_NOW, "loading1");
|
||||||
arrItem.name === name &&
|
}
|
||||||
arrItem.values &&
|
|
||||||
!arrItem.values.find((i) => i.value === values.value)
|
|
||||||
) {
|
|
||||||
arrItem.values.push(values);
|
|
||||||
}
|
|
||||||
|
|
||||||
let keys = arr.map((key) => {
|
function pintuanBuy() {
|
||||||
return key.name;
|
startCheckout(E_COUPON_CART_PINTUAN, "loadingPintuan");
|
||||||
});
|
}
|
||||||
if (!keys.includes(name)) {
|
|
||||||
arr.push({
|
function pointPay() {
|
||||||
name: name,
|
startCheckout(E_COUPON_CART_POINTS, "loading");
|
||||||
values: [values],
|
}
|
||||||
});
|
|
||||||
}
|
function startCheckout(action, loadingKey) {
|
||||||
});
|
const params = {
|
||||||
|
num: count.value,
|
||||||
|
skuId: skuDetail.value.id,
|
||||||
|
cartType: "BUY_NOW",
|
||||||
|
};
|
||||||
|
if (skuDetail.value.goodsType === "VIRTUAL_GOODS") {
|
||||||
|
params.cartType = "VIRTUAL";
|
||||||
|
} else if (isECouponGoods.value) {
|
||||||
|
params.cartType = resolveECouponCartType({
|
||||||
|
routeWay: route.query.way,
|
||||||
|
action,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
loadings[loadingKey].value = true;
|
||||||
|
addCartGoods(params)
|
||||||
|
.then((res) => {
|
||||||
|
loadings[loadingKey].value = false;
|
||||||
|
if (res.success) {
|
||||||
|
router.push({
|
||||||
|
path: "/pay",
|
||||||
|
query: { way: params.cartType },
|
||||||
});
|
});
|
||||||
});
|
} else {
|
||||||
arr.shift();
|
Message.warning(res.message);
|
||||||
this.formatList = arr;
|
|
||||||
|
|
||||||
let cur = list.filter((i) => i.skuId === this.$route.query.skuId)[0];
|
|
||||||
if (cur) {
|
|
||||||
cur.specValues
|
|
||||||
.filter((i) => i.specName !== "images")
|
|
||||||
.forEach((value, _index) => {
|
|
||||||
this.currentSelceted[_index] = value.specValue;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
this.skuList = list;
|
})
|
||||||
},
|
.catch(() => {
|
||||||
receiveCoupon(id) {
|
loadings[loadingKey].value = false;
|
||||||
// 领取优惠券
|
});
|
||||||
receiveCoupon(id).then((res) => {
|
}
|
||||||
if (res.success) {
|
|
||||||
Message.success("优惠券领取成功");
|
|
||||||
} else {
|
|
||||||
Message.warning(res.message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
promotion() {
|
|
||||||
this.promotionMap = createEmptyPromotionMap();
|
|
||||||
if (!this.detail || !this.detail.promotionMap) return false;
|
|
||||||
const keysArr = Object.keys(this.detail.promotionMap);
|
|
||||||
if (keysArr.length === 0) return false;
|
|
||||||
|
|
||||||
for (let i = 0; i < keysArr.length; i++) {
|
async function collect() {
|
||||||
const key = keysArr[i].split("-")[0];
|
if (!storage.getItem("accessToken") || !storage.getItem("userInfo")) {
|
||||||
if (key === "COUPON") {
|
router.push({
|
||||||
if (!Array.isArray(this.promotionMap.COUPON)) {
|
path: "/login",
|
||||||
this.promotionMap.COUPON = [];
|
query: {
|
||||||
}
|
rePath: route.path,
|
||||||
this.promotionMap.COUPON.push(this.detail.promotionMap[keysArr[i]]);
|
query: JSON.stringify(route.query || {}),
|
||||||
} else if (Object.prototype.hasOwnProperty.call(this.promotionMap, key)) {
|
},
|
||||||
this.promotionMap[key] = this.detail.promotionMap[keysArr[i]];
|
});
|
||||||
} else {
|
return;
|
||||||
this.promotionMap[key] = this.detail.promotionMap[keysArr[i]];
|
}
|
||||||
}
|
// 收藏商品
|
||||||
}
|
if (isCollected.value) {
|
||||||
},
|
let cancel = await cancelCollect("GOODS", skuDetail.value.id);
|
||||||
// 相册里会混进规格 JSON,只留图片地址;没有相册时用主图
|
if (cancel.success) {
|
||||||
swiperGoodsImg() {
|
Message.success("取消收藏成功");
|
||||||
const source = this.skuDetail.goodsGalleryList || [];
|
isCollected.value = false;
|
||||||
const gallery = Array.from(source).filter((item) => {
|
}
|
||||||
if (typeof item !== "string") return false;
|
} else {
|
||||||
return item.indexOf('"url":') === -1 && item.indexOf('"status":') === -1;
|
let collect = await collectGoods("GOODS", skuDetail.value.id);
|
||||||
});
|
if (collect.code === 200) {
|
||||||
if (gallery.length) {
|
isCollected.value = true;
|
||||||
this.imgList = gallery;
|
Message.success("收藏商品成功,可以前往个人中心我的收藏查看");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化数据
|
||||||
|
function formatSku(list) {
|
||||||
|
let arr = [{}];
|
||||||
|
list.forEach((item, index) => {
|
||||||
|
item.specValues.forEach((spec, specIndex) => {
|
||||||
|
let name = spec.specName;
|
||||||
|
let values = {
|
||||||
|
value: spec.specValue,
|
||||||
|
quantity: item.quantity,
|
||||||
|
};
|
||||||
|
if (name === "images") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.imgList = this.skuDetail.original ? [this.skuDetail.original] : [];
|
|
||||||
},
|
arr.forEach((arrItem, arrIndex) => {
|
||||||
},
|
if (
|
||||||
mounted() {
|
arrItem.name === name &&
|
||||||
// 用户登录才会判断是否收藏(须同时有 token,避免残留 userInfo 触发 401 跳转登录)
|
arrItem.values &&
|
||||||
if (this.Cookies.getItem("accessToken") && this.Cookies.getItem("userInfo")) {
|
!arrItem.values.find((i) => i.value === values.value)
|
||||||
isCollection("GOODS", this.skuDetail.id).then((res) => {
|
) {
|
||||||
if (res.success && res.result) {
|
arrItem.values.push(values);
|
||||||
this.isCollected = true;
|
}
|
||||||
|
|
||||||
|
let keys = arr.map((key) => {
|
||||||
|
return key.name;
|
||||||
|
});
|
||||||
|
if (!keys.includes(name)) {
|
||||||
|
arr.push({
|
||||||
|
name: name,
|
||||||
|
values: [values],
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
});
|
||||||
|
arr.shift();
|
||||||
|
formatList.value = arr;
|
||||||
|
|
||||||
this.formatSku(this.goodsSpecList);
|
let cur = list.filter((i) => i.skuId === route.query.skuId)[0];
|
||||||
// this.promotion();
|
if (cur) {
|
||||||
document.title = this.skuDetail.goodsName;
|
cur.specValues
|
||||||
},
|
.filter((i) => i.specName !== "images")
|
||||||
};
|
.forEach((value, _index) => {
|
||||||
|
currentSelceted.value[_index] = value.specValue;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
skuList.value = list;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleReceiveCoupon(id) {
|
||||||
|
// 领取优惠券
|
||||||
|
receiveCoupon(id).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
Message.success("优惠券领取成功");
|
||||||
|
} else {
|
||||||
|
Message.warning(res.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function promotion() {
|
||||||
|
promotionMap.value = createEmptyPromotionMap();
|
||||||
|
if (!props.detail || !props.detail.promotionMap) return false;
|
||||||
|
const keysArr = Object.keys(props.detail.promotionMap);
|
||||||
|
if (keysArr.length === 0) return false;
|
||||||
|
|
||||||
|
for (let i = 0; i < keysArr.length; i++) {
|
||||||
|
const key = keysArr[i].split("-")[0];
|
||||||
|
if (key === "COUPON") {
|
||||||
|
if (!Array.isArray(promotionMap.value.COUPON)) {
|
||||||
|
promotionMap.value.COUPON = [];
|
||||||
|
}
|
||||||
|
promotionMap.value.COUPON.push(props.detail.promotionMap[keysArr[i]]);
|
||||||
|
} else if (Object.prototype.hasOwnProperty.call(promotionMap.value, key)) {
|
||||||
|
promotionMap.value[key] = props.detail.promotionMap[keysArr[i]];
|
||||||
|
} else {
|
||||||
|
promotionMap.value[key] = props.detail.promotionMap[keysArr[i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 相册里会混进规格 JSON,只留图片地址;没有相册时用主图
|
||||||
|
function swiperGoodsImg() {
|
||||||
|
const source = skuDetail.value.goodsGalleryList || [];
|
||||||
|
const gallery = Array.from(source).filter((item) => {
|
||||||
|
if (typeof item !== "string") return false;
|
||||||
|
return item.indexOf('"url":') === -1 && item.indexOf('"status":') === -1;
|
||||||
|
});
|
||||||
|
if (gallery.length) {
|
||||||
|
imgList.value = gallery;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
imgList.value = skuDetail.value.original ? [skuDetail.value.original] : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 用户登录才会判断是否收藏(须同时有 token,避免残留 userInfo 触发 401 跳转登录)
|
||||||
|
if (storage.getItem("accessToken") && storage.getItem("userInfo")) {
|
||||||
|
isCollection("GOODS", skuDetail.value.id).then((res) => {
|
||||||
|
if (res.success && res.result) {
|
||||||
|
isCollected.value = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
formatSku(goodsSpecList.value);
|
||||||
|
// promotion();
|
||||||
|
document.title = skuDetail.value.goodsName;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -132,160 +132,162 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { computed, onMounted, onBeforeUnmount, reactive, ref } from 'vue';
|
||||||
import { Refresh } from '@element-plus/icons-vue';
|
import { Refresh } from '@element-plus/icons-vue';
|
||||||
import { goodsComment, goodsCommentNum } from '@/api/member.js';
|
import { goodsComment, goodsCommentNum } from '@/api/member.js';
|
||||||
import StoreSide from './StoreSide.vue';
|
import StoreSide from './StoreSide.vue';
|
||||||
|
|
||||||
export default {
|
defineOptions({ name: 'ShowGoodsDetail' });
|
||||||
components: { Refresh, StoreSide },
|
|
||||||
name: 'ShowGoodsDetail',
|
|
||||||
props: {
|
|
||||||
detail: {
|
|
||||||
type: Object,
|
|
||||||
default: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
detailTabs: [
|
|
||||||
{ name: 'intro', label: '商品介绍' },
|
|
||||||
{ name: 'comment', label: '商品评价' },
|
|
||||||
{ name: 'params', label: '商品参数' }
|
|
||||||
],
|
|
||||||
activeDetailTab: 'intro',
|
|
||||||
anchorStuck: false,
|
|
||||||
lockScrollSpy: false,
|
|
||||||
scrollTimer: null,
|
|
||||||
commentList: [],
|
|
||||||
commentParams: {
|
|
||||||
pageNumber: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
grade: '',
|
|
||||||
goodsId: ''
|
|
||||||
},
|
|
||||||
commentTypeNum: {},
|
|
||||||
commentTotal: 0
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
skuDetail () {
|
|
||||||
return this.detail.data;
|
|
||||||
},
|
|
||||||
goodsParamsList () {
|
|
||||||
const list = this.detail && Array.isArray(this.detail.goodsParamsDTOList) ? this.detail.goodsParamsDTOList : [];
|
|
||||||
const flat = [];
|
|
||||||
|
|
||||||
list.forEach((item) => {
|
const props = defineProps({
|
||||||
if (!item) return;
|
detail: {
|
||||||
if (Array.isArray(item.goodsParamsItemDTOList)) {
|
type: Object,
|
||||||
flat.push(...item.goodsParamsItemDTOList);
|
default: null
|
||||||
} else {
|
|
||||||
flat.push(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const cleaned = flat.filter((p) => p && (p.paramName || p.paramId));
|
|
||||||
const hasSort = cleaned.some((p) => p && p.sort !== undefined && p.sort !== null);
|
|
||||||
if (!hasSort) return cleaned;
|
|
||||||
|
|
||||||
return cleaned.slice().sort((a, b) => {
|
|
||||||
const sa = Number(a && a.sort) || 0;
|
|
||||||
const sb = Number(b && b.sort) || 0;
|
|
||||||
return sa - sb;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getNavOffset () {
|
|
||||||
const nav = this.$refs.tabNav;
|
|
||||||
return (nav ? nav.offsetHeight : 52) + 8;
|
|
||||||
},
|
|
||||||
scrollToSection (name) {
|
|
||||||
const el = this.$refs[name];
|
|
||||||
if (!el) return;
|
|
||||||
this.activeDetailTab = name;
|
|
||||||
this.lockScrollSpy = true;
|
|
||||||
const top = el.getBoundingClientRect().top + window.pageYOffset - this.getNavOffset();
|
|
||||||
window.scrollTo({ top: Math.max(0, top), behavior: 'smooth' });
|
|
||||||
const unlock = () => {
|
|
||||||
this.lockScrollSpy = false;
|
|
||||||
window.removeEventListener('scrollend', unlock);
|
|
||||||
};
|
|
||||||
window.addEventListener('scrollend', unlock);
|
|
||||||
window.clearTimeout(this.scrollTimer);
|
|
||||||
this.scrollTimer = window.setTimeout(unlock, 900);
|
|
||||||
},
|
|
||||||
handleScroll () {
|
|
||||||
const nav = this.$refs.tabNav;
|
|
||||||
if (nav) {
|
|
||||||
this.anchorStuck = nav.getBoundingClientRect().top <= 1;
|
|
||||||
}
|
|
||||||
if (this.lockScrollSpy) return;
|
|
||||||
const offset = this.getNavOffset() + 16;
|
|
||||||
let current = '';
|
|
||||||
this.detailTabs.forEach((tab) => {
|
|
||||||
const el = this.$refs[tab.name];
|
|
||||||
if (el && el.getBoundingClientRect().top <= offset) {
|
|
||||||
current = tab.name;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (current) {
|
|
||||||
this.activeDetailTab = current;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
changePageNum (val) {
|
|
||||||
this.commentParams.pageNumber = val;
|
|
||||||
this.getList();
|
|
||||||
},
|
|
||||||
changePageSize (val) {
|
|
||||||
this.commentParams.pageNumber = 1;
|
|
||||||
this.commentParams.pageSize = val;
|
|
||||||
this.getList();
|
|
||||||
},
|
|
||||||
getList () {
|
|
||||||
this.commentParams.goodsId = this.skuDetail.goodsId;
|
|
||||||
goodsComment(this.commentParams).then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
this.commentList = res.result.records;
|
|
||||||
this.commentTotal = res.result.total;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
goodsCommentNum(this.skuDetail.goodsId).then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
this.commentTypeNum = res.result;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
viewByGrade (grade) {
|
|
||||||
this.commentParams.grade = grade;
|
|
||||||
this.commentParams.pageNumber = 1;
|
|
||||||
this.getList();
|
|
||||||
},
|
|
||||||
previewImg (img, item) {
|
|
||||||
item.previewImg = img;
|
|
||||||
item.deg = item.deg || 0;
|
|
||||||
},
|
|
||||||
hidePreviewImg (item) {
|
|
||||||
item.previewImg = '';
|
|
||||||
},
|
|
||||||
rotatePreviewImg (type, item) {
|
|
||||||
const current = item.deg || 0;
|
|
||||||
item.deg = type ? current + 90 : current - 90;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
window.addEventListener('scroll', this.handleScroll, { passive: true });
|
|
||||||
this.getList();
|
|
||||||
if (this.skuDetail.grade === null || this.skuDetail.grade === undefined) {
|
|
||||||
this.skuDetail.grade = 100;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
beforeUnmount () {
|
|
||||||
window.removeEventListener('scroll', this.handleScroll);
|
|
||||||
window.clearTimeout(this.scrollTimer);
|
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
|
const tabNav = ref(null);
|
||||||
|
const intro = ref(null);
|
||||||
|
const comment = ref(null);
|
||||||
|
const params = ref(null);
|
||||||
|
const sectionRefs = { intro, comment, params };
|
||||||
|
|
||||||
|
const detailTabs = ref([
|
||||||
|
{ name: 'intro', label: '商品介绍' },
|
||||||
|
{ name: 'comment', label: '商品评价' },
|
||||||
|
{ name: 'params', label: '商品参数' }
|
||||||
|
]);
|
||||||
|
const activeDetailTab = ref('intro');
|
||||||
|
const anchorStuck = ref(false);
|
||||||
|
const lockScrollSpy = ref(false);
|
||||||
|
let scrollTimer = null;
|
||||||
|
const commentList = ref([]);
|
||||||
|
const commentParams = reactive({
|
||||||
|
pageNumber: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
grade: '',
|
||||||
|
goodsId: ''
|
||||||
|
});
|
||||||
|
const commentTypeNum = ref({});
|
||||||
|
const commentTotal = ref(0);
|
||||||
|
|
||||||
|
const skuDetail = computed(() => {
|
||||||
|
return props.detail.data;
|
||||||
|
});
|
||||||
|
const goodsParamsList = computed(() => {
|
||||||
|
const list = props.detail && Array.isArray(props.detail.goodsParamsDTOList) ? props.detail.goodsParamsDTOList : [];
|
||||||
|
const flat = [];
|
||||||
|
|
||||||
|
list.forEach((item) => {
|
||||||
|
if (!item) return;
|
||||||
|
if (Array.isArray(item.goodsParamsItemDTOList)) {
|
||||||
|
flat.push(...item.goodsParamsItemDTOList);
|
||||||
|
} else {
|
||||||
|
flat.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const cleaned = flat.filter((p) => p && (p.paramName || p.paramId));
|
||||||
|
const hasSort = cleaned.some((p) => p && p.sort !== undefined && p.sort !== null);
|
||||||
|
if (!hasSort) return cleaned;
|
||||||
|
|
||||||
|
return cleaned.slice().sort((a, b) => {
|
||||||
|
const sa = Number(a && a.sort) || 0;
|
||||||
|
const sb = Number(b && b.sort) || 0;
|
||||||
|
return sa - sb;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function getNavOffset () {
|
||||||
|
const nav = tabNav.value;
|
||||||
|
return (nav ? nav.offsetHeight : 52) + 8;
|
||||||
|
}
|
||||||
|
function scrollToSection (name) {
|
||||||
|
const el = sectionRefs[name] && sectionRefs[name].value;
|
||||||
|
if (!el) return;
|
||||||
|
activeDetailTab.value = name;
|
||||||
|
lockScrollSpy.value = true;
|
||||||
|
const top = el.getBoundingClientRect().top + window.pageYOffset - getNavOffset();
|
||||||
|
window.scrollTo({ top: Math.max(0, top), behavior: 'smooth' });
|
||||||
|
const unlock = () => {
|
||||||
|
lockScrollSpy.value = false;
|
||||||
|
window.removeEventListener('scrollend', unlock);
|
||||||
|
};
|
||||||
|
window.addEventListener('scrollend', unlock);
|
||||||
|
window.clearTimeout(scrollTimer);
|
||||||
|
scrollTimer = window.setTimeout(unlock, 900);
|
||||||
|
}
|
||||||
|
function handleScroll () {
|
||||||
|
const nav = tabNav.value;
|
||||||
|
if (nav) {
|
||||||
|
anchorStuck.value = nav.getBoundingClientRect().top <= 1;
|
||||||
|
}
|
||||||
|
if (lockScrollSpy.value) return;
|
||||||
|
const offset = getNavOffset() + 16;
|
||||||
|
let current = '';
|
||||||
|
detailTabs.value.forEach((tab) => {
|
||||||
|
const el = sectionRefs[tab.name] && sectionRefs[tab.name].value;
|
||||||
|
if (el && el.getBoundingClientRect().top <= offset) {
|
||||||
|
current = tab.name;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (current) {
|
||||||
|
activeDetailTab.value = current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function changePageNum (val) {
|
||||||
|
commentParams.pageNumber = val;
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
function changePageSize (val) {
|
||||||
|
commentParams.pageNumber = 1;
|
||||||
|
commentParams.pageSize = val;
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
function getList () {
|
||||||
|
commentParams.goodsId = skuDetail.value.goodsId;
|
||||||
|
goodsComment(commentParams).then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
commentList.value = res.result.records;
|
||||||
|
commentTotal.value = res.result.total;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
goodsCommentNum(skuDetail.value.goodsId).then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
commentTypeNum.value = res.result;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function viewByGrade (grade) {
|
||||||
|
commentParams.grade = grade;
|
||||||
|
commentParams.pageNumber = 1;
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
function previewImg (img, item) {
|
||||||
|
item.previewImg = img;
|
||||||
|
item.deg = item.deg || 0;
|
||||||
|
}
|
||||||
|
function hidePreviewImg (item) {
|
||||||
|
item.previewImg = '';
|
||||||
|
}
|
||||||
|
function rotatePreviewImg (type, item) {
|
||||||
|
const current = item.deg || 0;
|
||||||
|
item.deg = type ? current + 90 : current - 90;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('scroll', handleScroll, { passive: true });
|
||||||
|
getList();
|
||||||
|
if (skuDetail.value.grade === null || skuDetail.value.grade === undefined) {
|
||||||
|
skuDetail.value.grade = 100;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.removeEventListener('scroll', handleScroll);
|
||||||
|
window.clearTimeout(scrollTimer);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -65,149 +65,217 @@
|
|||||||
</aside>
|
</aside>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import { Message } from "@/utils/message";
|
import { computed, ref, watch } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
|
import { Message, Modal } from "@/utils/message";
|
||||||
import { goodsList } from "@/api/goods";
|
import { goodsList } from "@/api/goods";
|
||||||
import { getStoreById } from "@/api/shopentry";
|
import { getStoreById } from "@/api/shopentry";
|
||||||
import { cancelStoreCollect, collectStore, isStoreCollection } from "@/api/member";
|
import { cancelStoreCollect, collectStore, isStoreCollection } from "@/api/member";
|
||||||
import imTalk from "@/components/mixes/talkIm";
|
import { getIMDetail } from "@/api/common";
|
||||||
|
import { getMemberMsg } from "@/api/login";
|
||||||
|
import storage from "@/plugins/storage.js";
|
||||||
|
|
||||||
export default {
|
defineOptions({ name: "StoreSide" });
|
||||||
name: "StoreSide",
|
|
||||||
mixins: [imTalk],
|
const props = defineProps({
|
||||||
props: {
|
storeId: {
|
||||||
storeId: {
|
type: [String, Number],
|
||||||
type: [String, Number],
|
default: "",
|
||||||
default: "",
|
|
||||||
},
|
|
||||||
goodsId: {
|
|
||||||
type: [String, Number],
|
|
||||||
default: "",
|
|
||||||
},
|
|
||||||
skuId: {
|
|
||||||
type: [String, Number],
|
|
||||||
default: "",
|
|
||||||
},
|
|
||||||
storeNameFallback: {
|
|
||||||
type: String,
|
|
||||||
default: "",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
data() {
|
goodsId: {
|
||||||
return {
|
type: [String, Number],
|
||||||
store: {},
|
default: "",
|
||||||
logoError: false,
|
|
||||||
collected: false,
|
|
||||||
recommends: [],
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
computed: {
|
skuId: {
|
||||||
storeName() {
|
type: [String, Number],
|
||||||
return this.store.storeName || this.storeNameFallback || "店铺";
|
default: "",
|
||||||
},
|
|
||||||
storeInitial() {
|
|
||||||
return this.storeName.slice(0, 1);
|
|
||||||
},
|
|
||||||
// 自营标记后端可能是 true、1 或 "1"
|
|
||||||
isSelfOperated() {
|
|
||||||
const flag = this.store.selfOperated;
|
|
||||||
return flag === true || flag === 1 || flag === "1";
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
watch: {
|
storeNameFallback: {
|
||||||
storeId: {
|
type: String,
|
||||||
immediate: true,
|
default: "",
|
||||||
handler(id) {
|
},
|
||||||
if (!id) return;
|
});
|
||||||
this.logoError = false;
|
|
||||||
this.loadStore(id);
|
const route = useRoute();
|
||||||
this.loadRecommend(id);
|
const router = useRouter();
|
||||||
this.loadCollected(id);
|
|
||||||
|
const store = ref({});
|
||||||
|
const logoError = ref(false);
|
||||||
|
const collected = ref(false);
|
||||||
|
const recommends = ref([]);
|
||||||
|
// 原 talkIm mixin 的 IM 链接
|
||||||
|
const IMLink = ref("");
|
||||||
|
|
||||||
|
const storeName = computed(() => {
|
||||||
|
return store.value.storeName || props.storeNameFallback || "店铺";
|
||||||
|
});
|
||||||
|
const storeInitial = computed(() => {
|
||||||
|
return storeName.value.slice(0, 1);
|
||||||
|
});
|
||||||
|
// 自营标记后端可能是 true、1 或 "1"
|
||||||
|
const isSelfOperated = computed(() => {
|
||||||
|
const flag = store.value.selfOperated;
|
||||||
|
return flag === true || flag === 1 || flag === "1";
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.storeId,
|
||||||
|
(id) => {
|
||||||
|
if (!id) return;
|
||||||
|
logoError.value = false;
|
||||||
|
loadStore(id);
|
||||||
|
loadRecommend(id);
|
||||||
|
loadCollected(id);
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
// 没有评分或小于等于 0 时按满分展示
|
||||||
|
function formatScore(value) {
|
||||||
|
const score = Number(value);
|
||||||
|
if (!Number.isFinite(score) || score <= 0) return "5.00";
|
||||||
|
return score.toFixed(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadStore(id) {
|
||||||
|
getStoreById(id).then((res) => {
|
||||||
|
if (res.success && res.result) store.value = res.result;
|
||||||
|
}).catch((err) => {
|
||||||
|
console.warn('获取店铺信息失败', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按销量取店内商品,去掉当前商品,最多 4 个
|
||||||
|
function loadRecommend(id) {
|
||||||
|
goodsList({
|
||||||
|
storeId: id,
|
||||||
|
pageNumber: 1,
|
||||||
|
pageSize: 8,
|
||||||
|
sort: "buyCount",
|
||||||
|
order: "desc",
|
||||||
|
}).then((res) => {
|
||||||
|
const records = (res.success && res.result && res.result.records) || [];
|
||||||
|
recommends.value = records
|
||||||
|
.filter((item) => String(item.goodsId) !== String(props.goodsId))
|
||||||
|
.slice(0, 4);
|
||||||
|
}).catch(() => {
|
||||||
|
// 推荐商品为可选数据,加载失败时静默
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 同时有 token 和用户信息才查收藏,避免残留 userInfo 触发登录跳转
|
||||||
|
function loadCollected(id) {
|
||||||
|
if (!storage.getItem("accessToken") || !storage.getItem("userInfo")) return;
|
||||||
|
isStoreCollection("STORE", id).then((res) => {
|
||||||
|
if (res.success && res.result) collected.value = true;
|
||||||
|
}).catch(() => {
|
||||||
|
// 收藏状态为可选数据,失败时静默
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function goStore() {
|
||||||
|
router.push({ path: "/merchant", query: { id: props.storeId } });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 推荐商品新开详情页,店铺页仍在当前页打开
|
||||||
|
function goGoods(item) {
|
||||||
|
const href = router.resolve({
|
||||||
|
path: "/goodsDetail",
|
||||||
|
query: { skuId: item.id, goodsId: item.goodsId },
|
||||||
|
}).href;
|
||||||
|
window.open(href, "_blank");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 原 talkIm mixin 的登录提示
|
||||||
|
function promptLogin() {
|
||||||
|
Modal.confirm({
|
||||||
|
title: "温馨提示",
|
||||||
|
content: "请登录后执行此操作",
|
||||||
|
okText: "立即登录",
|
||||||
|
cancelText: "取消",
|
||||||
|
onOk: () => {
|
||||||
|
router.push({
|
||||||
|
path: "/login",
|
||||||
|
query: {
|
||||||
|
rePath: route.path,
|
||||||
|
query: JSON.stringify(route.query || {}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取im信息(原 talkIm mixin)
|
||||||
|
async function getIMDetailMethods() {
|
||||||
|
let res = await getIMDetail();
|
||||||
|
if (res.success) {
|
||||||
|
IMLink.value = res.result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳转im客服(原 talkIm mixin;本组件无 storeMsg,mixin 的 storeMsg?.storeId 兜底恒为 undefined,等价于直接用传入的 id)
|
||||||
|
async function IMService(id, goodsId, skuId) {
|
||||||
|
if (!storage.getItem("accessToken")) {
|
||||||
|
promptLogin();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await getIMDetailMethods();
|
||||||
|
const userInfo = await getMemberMsg();
|
||||||
|
if (!userInfo.success) {
|
||||||
|
promptLogin();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const accessToken = storage.getItem("accessToken");
|
||||||
|
const storeId = id;
|
||||||
|
const baseUrl = `${IMLink.value}?token=${accessToken}&id=${storeId}`;
|
||||||
|
|
||||||
|
if (goodsId && skuId) {
|
||||||
|
window.open(`${baseUrl}&goodsId=${goodsId}&skuId=${skuId}`);
|
||||||
|
} else {
|
||||||
|
window.open(baseUrl);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// token 过期等场景由 request 拦截器处理;未登录时避免未捕获异常
|
||||||
|
if (!storage.getItem("accessToken")) {
|
||||||
|
promptLogin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function contact() {
|
||||||
|
IMService(props.storeId, props.goodsId, props.skuId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 未登录先去登录,并带回当前商品页
|
||||||
|
async function toggleCollect() {
|
||||||
|
if (!storage.getItem("accessToken") || !storage.getItem("userInfo")) {
|
||||||
|
router.push({
|
||||||
|
path: "/login",
|
||||||
|
query: {
|
||||||
|
rePath: route.path,
|
||||||
|
query: JSON.stringify(route.query || {}),
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
},
|
return;
|
||||||
methods: {
|
}
|
||||||
// 没有评分或小于等于 0 时按满分展示
|
if (collected.value) {
|
||||||
formatScore(value) {
|
const cancel = await cancelStoreCollect("STORE", props.storeId);
|
||||||
const score = Number(value);
|
if (cancel.success) {
|
||||||
if (!Number.isFinite(score) || score <= 0) return "5.00";
|
collected.value = false;
|
||||||
return score.toFixed(2);
|
Message.success("已取消收藏");
|
||||||
},
|
}
|
||||||
loadStore(id) {
|
return;
|
||||||
getStoreById(id).then((res) => {
|
}
|
||||||
if (res.success && res.result) this.store = res.result;
|
const collect = await collectStore("STORE", props.storeId);
|
||||||
}).catch((err) => {
|
if (collect.code === 200) {
|
||||||
console.warn('获取店铺信息失败', err);
|
collected.value = true;
|
||||||
});
|
Message.success("收藏店铺成功,可以前往个人中心我的收藏查看");
|
||||||
},
|
}
|
||||||
// 按销量取店内商品,去掉当前商品,最多 4 个
|
}
|
||||||
loadRecommend(id) {
|
|
||||||
goodsList({
|
|
||||||
storeId: id,
|
|
||||||
pageNumber: 1,
|
|
||||||
pageSize: 8,
|
|
||||||
sort: "buyCount",
|
|
||||||
order: "desc",
|
|
||||||
}).then((res) => {
|
|
||||||
const records = (res.success && res.result && res.result.records) || [];
|
|
||||||
this.recommends = records
|
|
||||||
.filter((item) => String(item.goodsId) !== String(this.goodsId))
|
|
||||||
.slice(0, 4);
|
|
||||||
}).catch(() => {
|
|
||||||
// 推荐商品为可选数据,加载失败时静默
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 同时有 token 和用户信息才查收藏,避免残留 userInfo 触发登录跳转
|
|
||||||
loadCollected(id) {
|
|
||||||
if (!this.Cookies.getItem("accessToken") || !this.Cookies.getItem("userInfo")) return;
|
|
||||||
isStoreCollection("STORE", id).then((res) => {
|
|
||||||
if (res.success && res.result) this.collected = true;
|
|
||||||
}).catch(() => {
|
|
||||||
// 收藏状态为可选数据,失败时静默
|
|
||||||
});
|
|
||||||
},
|
|
||||||
goStore() {
|
|
||||||
this.$router.push({ path: "/merchant", query: { id: this.storeId } });
|
|
||||||
},
|
|
||||||
// 推荐商品新开详情页,店铺页仍在当前页打开
|
|
||||||
goGoods(item) {
|
|
||||||
const href = this.$router.resolve({
|
|
||||||
path: "/goodsDetail",
|
|
||||||
query: { skuId: item.id, goodsId: item.goodsId },
|
|
||||||
}).href;
|
|
||||||
window.open(href, "_blank");
|
|
||||||
},
|
|
||||||
contact() {
|
|
||||||
this.IMService(this.storeId, this.goodsId, this.skuId);
|
|
||||||
},
|
|
||||||
// 未登录先去登录,并带回当前商品页
|
|
||||||
async toggleCollect() {
|
|
||||||
if (!this.Cookies.getItem("accessToken") || !this.Cookies.getItem("userInfo")) {
|
|
||||||
this.$router.push({
|
|
||||||
path: "/login",
|
|
||||||
query: {
|
|
||||||
rePath: this.$route.path,
|
|
||||||
query: JSON.stringify(this.$route.query || {}),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.collected) {
|
|
||||||
const cancel = await cancelStoreCollect("STORE", this.storeId);
|
|
||||||
if (cancel.success) {
|
|
||||||
this.collected = false;
|
|
||||||
Message.success("已取消收藏");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const collect = await collectStore("STORE", this.storeId);
|
|
||||||
if (collect.code === 200) {
|
|
||||||
this.collected = true;
|
|
||||||
Message.success("收藏店铺成功,可以前往个人中心我的收藏查看");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -90,7 +90,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { ref, computed } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { Modal } from "@/utils/message";
|
import { Modal } from "@/utils/message";
|
||||||
import { ShoppingCart } from '@element-plus/icons-vue';
|
import { ShoppingCart } from '@element-plus/icons-vue';
|
||||||
import storage from '@/plugins/storage.js';
|
import storage from '@/plugins/storage.js';
|
||||||
@@ -98,149 +100,145 @@ import {cartGoodsAll} from '@/api/cart.js';
|
|||||||
import {logout} from '@/api/account.js';
|
import {logout} from '@/api/account.js';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import { refreshUnreadMessage } from '@/utils/unreadMessage';
|
import { refreshUnreadMessage } from '@/utils/unreadMessage';
|
||||||
|
import { useAppStore } from '@/stores/app';
|
||||||
|
|
||||||
export default {
|
defineOptions({ name: 'M-Header' });
|
||||||
components: { ShoppingCart },
|
|
||||||
name: 'M-Header',
|
|
||||||
created() {
|
|
||||||
if (storage.getItem('userInfo')) {
|
|
||||||
this.userInfo = JSON.parse(storage.getItem('userInfo'));
|
|
||||||
refreshUnreadMessage();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
const route = useRoute();
|
||||||
return {
|
const router = useRouter();
|
||||||
config,
|
const appStore = useAppStore();
|
||||||
userInfo: {}, // 用户信息
|
|
||||||
shoppingCart: [], // 购物车
|
const userInfo = ref({}); // 用户信息
|
||||||
cartLoading: false,
|
const shoppingCart = ref([]); // 购物车
|
||||||
cartLoadedAt: 0,
|
const cartLoading = ref(false);
|
||||||
cartCacheTime: 5000
|
const cartLoadedAt = ref(0);
|
||||||
};
|
const cartCacheTime = ref(5000);
|
||||||
},
|
|
||||||
computed: {
|
// 购物车商品数量
|
||||||
// 购物车商品数量
|
const cartNum = computed(() => appStore.cartNum);
|
||||||
cartNum() {
|
const unreadMsg = computed(() => appStore.unreadMsg);
|
||||||
return this.$store.state.cartNum;
|
const unreadLabel = computed(() => {
|
||||||
},
|
return unreadMsg.value > 99 ? '99+' : String(unreadMsg.value);
|
||||||
unreadMsg() {
|
});
|
||||||
return Number(this.$store.state.unreadMsg) || 0;
|
|
||||||
},
|
if (storage.getItem('userInfo')) {
|
||||||
unreadLabel() {
|
userInfo.value = JSON.parse(storage.getItem('userInfo'));
|
||||||
return this.unreadMsg > 99 ? '99+' : String(this.unreadMsg);
|
refreshUnreadMessage();
|
||||||
}
|
}
|
||||||
},
|
|
||||||
methods: {
|
function goToPay() {
|
||||||
goToPay() {
|
if (!userInfo.value.username) {
|
||||||
if (!this.userInfo.username) {
|
Modal.confirm({
|
||||||
Modal.confirm({
|
title: '温馨提示',
|
||||||
title: '温馨提示',
|
content: '请登录后执行此操作',
|
||||||
content: '请登录后执行此操作',
|
okText: '立即登录',
|
||||||
okText: '立即登录',
|
cancelText: '取消',
|
||||||
cancelText: '取消',
|
onOk: () => {
|
||||||
onOk: () => {
|
router.push({
|
||||||
this.$router.push({
|
path: '/login',
|
||||||
path: '/login',
|
query: {
|
||||||
query: {
|
rePath: '/cart',
|
||||||
rePath: '/cart',
|
query: JSON.stringify(route.query || {}),
|
||||||
query: JSON.stringify(this.$route.query || {}),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return;
|
},
|
||||||
}
|
});
|
||||||
this.$router.push('/cart');
|
return;
|
||||||
},
|
}
|
||||||
myInfo() { // 跳转会员中心
|
router.push('/cart');
|
||||||
this.$router.push('/home');
|
}
|
||||||
},
|
|
||||||
signOutFun() { // 退出登录
|
function myInfo() { // 跳转会员中心
|
||||||
logout().then(res => {
|
router.push('/home');
|
||||||
storage.removeItem('accessToken');
|
}
|
||||||
storage.removeItem('refreshToken');
|
|
||||||
storage.removeItem('userInfo');
|
function signOutFun() { // 退出登录
|
||||||
storage.removeItem('cartNum');
|
logout().then(res => {
|
||||||
this.$store.commit('SET_CARTNUM', 0)
|
storage.removeItem('accessToken');
|
||||||
this.$store.commit('SET_UNREAD_MSG', 0)
|
storage.removeItem('refreshToken');
|
||||||
this.$router.push('/login');
|
storage.removeItem('userInfo');
|
||||||
});
|
storage.removeItem('cartNum');
|
||||||
},
|
appStore.setCartNum(0)
|
||||||
goUserCenter(path) {
|
appStore.setUnreadMsg(0)
|
||||||
// 跳转我的订单,我的足迹、收藏等
|
router.push('/login');
|
||||||
if (this.userInfo.username) {
|
});
|
||||||
this.$router.push({path: path});
|
}
|
||||||
} else {
|
|
||||||
Modal.confirm({
|
function goUserCenter(path) {
|
||||||
title: '请登录',
|
// 跳转我的订单,我的足迹、收藏等
|
||||||
content: '请登录后执行此操作',
|
if (userInfo.value.username) {
|
||||||
okText: '立即登录',
|
router.push({path: path});
|
||||||
cancelText: '继续浏览',
|
} else {
|
||||||
onOk: () => {
|
Modal.confirm({
|
||||||
this.$router.push({
|
title: '请登录',
|
||||||
path: '/login',
|
content: '请登录后执行此操作',
|
||||||
query: {
|
okText: '立即登录',
|
||||||
rePath: this.$route.path,
|
cancelText: '继续浏览',
|
||||||
query: JSON.stringify(this.$route.query || {}),
|
onOk: () => {
|
||||||
},
|
router.push({
|
||||||
});
|
path: '/login',
|
||||||
}
|
query: {
|
||||||
|
rePath: route.path,
|
||||||
|
query: JSON.stringify(route.query || {}),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
shopEntry() {
|
}
|
||||||
if (storage.getItem('accessToken')) {
|
}
|
||||||
this.$router.push({
|
|
||||||
path: '/shopEntry',
|
function shopEntry() {
|
||||||
query: { id: 1 },
|
if (storage.getItem('accessToken')) {
|
||||||
});
|
router.push({
|
||||||
return;
|
path: '/shopEntry',
|
||||||
}
|
query: { id: 1 },
|
||||||
Modal.confirm({
|
});
|
||||||
title: '温馨提示',
|
return;
|
||||||
content: '请登录后执行此操作',
|
}
|
||||||
okText: '立即登录',
|
Modal.confirm({
|
||||||
cancelText: '取消',
|
title: '温馨提示',
|
||||||
onOk: () => {
|
content: '请登录后执行此操作',
|
||||||
this.$router.push({
|
okText: '立即登录',
|
||||||
path: '/login',
|
cancelText: '取消',
|
||||||
query: {
|
onOk: () => {
|
||||||
rePath: '/shopEntry',
|
router.push({
|
||||||
query: JSON.stringify({ id: 1 }),
|
path: '/login',
|
||||||
},
|
query: {
|
||||||
});
|
rePath: '/shopEntry',
|
||||||
|
query: JSON.stringify({ id: 1 }),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleCartDropdownVisible(visible) {
|
});
|
||||||
if (visible) {
|
}
|
||||||
this.getCartList();
|
|
||||||
}
|
function handleCartDropdownVisible(visible) {
|
||||||
},
|
if (visible) {
|
||||||
getCartList() {
|
getCartList();
|
||||||
// 获取购物车列表
|
|
||||||
if (!this.userInfo.username || this.cartLoading) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const now = Date.now();
|
|
||||||
if (this.cartLoadedAt && now - this.cartLoadedAt < this.cartCacheTime) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.cartLoadedAt = now;
|
|
||||||
this.cartLoading = true;
|
|
||||||
cartGoodsAll().then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
this.shoppingCart = (res.result && res.result.skuList) || [];
|
|
||||||
this.$store.commit('SET_CARTNUM', this.shoppingCart.length);
|
|
||||||
this.Cookies.setItem('cartNum', this.shoppingCart.length);
|
|
||||||
}
|
|
||||||
}).finally(() => {
|
|
||||||
this.cartLoading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
function getCartList() {
|
||||||
|
// 获取购物车列表
|
||||||
|
if (!userInfo.value.username || cartLoading.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const now = Date.now();
|
||||||
|
if (cartLoadedAt.value && now - cartLoadedAt.value < cartCacheTime.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cartLoadedAt.value = now;
|
||||||
|
cartLoading.value = true;
|
||||||
|
cartGoodsAll().then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
shoppingCart.value = (res.result && res.result.skuList) || [];
|
||||||
|
appStore.setCartNum(shoppingCart.value.length);
|
||||||
|
storage.setItem('cartNum', shoppingCart.value.length);
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
cartLoading.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -24,44 +24,40 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
|
import { computed, onMounted, ref } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
import Search from '@/components/Search.vue'
|
import Search from '@/components/Search.vue'
|
||||||
import cateNav from '@/components/nav/CateNav.vue'
|
import cateNav from '@/components/nav/CateNav.vue'
|
||||||
import { cartCount } from '@/api/cart.js'
|
import { cartCount } from '@/api/cart.js'
|
||||||
import storage from '@/plugins/storage.js'
|
import storage from '@/plugins/storage.js'
|
||||||
import { ShoppingCart } from '@element-plus/icons-vue'
|
import { ShoppingCart } from '@element-plus/icons-vue'
|
||||||
|
import { useAppStore } from '@/stores/app'
|
||||||
|
|
||||||
export default {
|
const router = useRouter()
|
||||||
components: { ShoppingCart, Search, cateNav },
|
const appStore = useAppStore()
|
||||||
data() {
|
|
||||||
return {
|
const userInfo = ref({})
|
||||||
userInfo: {}
|
const cartNum = computed(() => appStore.cartNum)
|
||||||
}
|
|
||||||
},
|
function goCartList() {
|
||||||
computed: {
|
router.push('/cart')
|
||||||
cartNum() {
|
}
|
||||||
return this.$store.state.cartNum
|
|
||||||
}
|
function getCartList() {
|
||||||
},
|
if (storage.getItem('userInfo')) {
|
||||||
methods: {
|
cartCount().then((res) => {
|
||||||
goCartList() {
|
appStore.setCartNum(res.result)
|
||||||
this.$router.push('/cart')
|
storage.setItem('cartNum', res.result)
|
||||||
},
|
})
|
||||||
getCartList() {
|
|
||||||
if (storage.getItem('userInfo')) {
|
|
||||||
cartCount().then((res) => {
|
|
||||||
this.$store.commit('SET_CARTNUM', res.result)
|
|
||||||
this.Cookies.setItem('cartNum', res.result)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
if (storage.getItem('userInfo')) {
|
|
||||||
this.userInfo = JSON.parse(storage.getItem('userInfo'))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (storage.getItem('userInfo')) {
|
||||||
|
userInfo.value = JSON.parse(storage.getItem('userInfo'))
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
$side-col-width: 263.2px;
|
$side-col-width: 263.2px;
|
||||||
|
|||||||
@@ -11,29 +11,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
|
import { computed } from 'vue';
|
||||||
import ModelFormItem from './ModelFormItem.vue';
|
import ModelFormItem from './ModelFormItem.vue';
|
||||||
export default {
|
|
||||||
name: 'modelForm',
|
defineOptions({ name: 'modelForm' });
|
||||||
components: {
|
|
||||||
ModelFormItem
|
const props = defineProps(['data']);
|
||||||
},
|
|
||||||
props: ['data'],
|
const renderList = computed(() => {
|
||||||
computed: {
|
const list = props.data?.list || [];
|
||||||
renderList() {
|
return list.filter(
|
||||||
const list = this.data?.list || [];
|
(el) => el && el.key && !isBuiltinModule(el.type)
|
||||||
return list.filter(
|
);
|
||||||
(el) => el && el.key && !this.isBuiltinModule(el.type)
|
});
|
||||||
);
|
|
||||||
}
|
function isBuiltinModule(type) {
|
||||||
},
|
// 顶部广告、快捷导航由 Index 页单独渲染
|
||||||
methods: {
|
return type === 'topAdvert' || type === 'navBar';
|
||||||
isBuiltinModule(type) {
|
}
|
||||||
// 顶部广告、快捷导航由 Index 页单独渲染
|
|
||||||
return type === 'topAdvert' || type === 'navBar';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.model-form {
|
.model-form {
|
||||||
|
|||||||
@@ -138,7 +138,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { ref } from "vue";
|
||||||
import ModelCarousel from "./modelList/Carousel.vue";
|
import ModelCarousel from "./modelList/Carousel.vue";
|
||||||
import ModelCarousel1 from "./modelList/Carousel1.vue";
|
import ModelCarousel1 from "./modelList/Carousel1.vue";
|
||||||
import ModelCarousel2 from "./modelList/Carousel2.vue";
|
import ModelCarousel2 from "./modelList/Carousel2.vue";
|
||||||
@@ -152,34 +153,15 @@ import goodsAndType from "./modelList/goodsAndType.vue";
|
|||||||
import onlyGoodsModel from "./modelList/onlyGoodsModel.vue";
|
import onlyGoodsModel from "./modelList/onlyGoodsModel.vue";
|
||||||
import mixModel from "./modelList/mixModel.vue";
|
import mixModel from "./modelList/mixModel.vue";
|
||||||
import forYour from "./modelList/forYour.vue";
|
import forYour from "./modelList/forYour.vue";
|
||||||
import decorateImg from "@/assets/images/decorate.png";
|
import decorateImgUrl from "@/assets/images/decorate.png";
|
||||||
|
|
||||||
export default {
|
defineOptions({ name: "modelFormItem" });
|
||||||
name: "modelFormItem",
|
|
||||||
props: ["element", "select", "index", "data"],
|
defineProps(["element", "select", "index", "data"]);
|
||||||
components: {
|
|
||||||
ModelCarousel,
|
const decorateImg = ref(decorateImgUrl);
|
||||||
ModelCarousel1,
|
const showModal = ref(false); // 控制模态框显隐
|
||||||
ModelCarousel2,
|
const selected = ref({}); // 已选数据
|
||||||
Recommend,
|
|
||||||
NewGoodsSort,
|
|
||||||
FirstPageAdvert,
|
|
||||||
NotEnough,
|
|
||||||
Seckill,
|
|
||||||
oneRowThreeColumns,
|
|
||||||
goodsAndType,
|
|
||||||
onlyGoodsModel,
|
|
||||||
mixModel,
|
|
||||||
forYour
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
decorateImg,
|
|
||||||
showModal: false, // 控制模态框显隐
|
|
||||||
selected: {}, // 已选数据
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.model-item {
|
.model-item {
|
||||||
|
|||||||
@@ -82,151 +82,153 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { computed, onMounted, ref } from "vue";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
import { Modal } from "@/utils/message";
|
import { Modal } from "@/utils/message";
|
||||||
import storage from "@/plugins/storage";
|
import storage from "@/plugins/storage";
|
||||||
import config from "@/config";
|
import config from "@/config";
|
||||||
import defaultAvatar from "@/assets/images/default.png";
|
import defaultAvatar from "@/assets/images/default.png";
|
||||||
import { getIconUrl } from "@/assets/iconfont/iconMap";
|
import { getIconUrl } from "@/assets/iconfont/iconMap";
|
||||||
export default {
|
|
||||||
name: "modelCarousel",
|
defineOptions({ name: "modelCarousel" });
|
||||||
props: ["data"],
|
|
||||||
computed: {
|
const props = defineProps(["data"]);
|
||||||
carouselList() {
|
|
||||||
return this.data?.options?.list || [];
|
const router = useRouter();
|
||||||
},
|
|
||||||
displaySlides() {
|
const carouselList = computed(() => {
|
||||||
return this.carouselList.filter((item) => item && item.img);
|
return props.data?.options?.list || [];
|
||||||
},
|
});
|
||||||
|
|
||||||
|
const displaySlides = computed(() => {
|
||||||
|
return carouselList.value.filter((item) => item && item.img);
|
||||||
|
});
|
||||||
|
|
||||||
|
const userInfo = ref({}); // 用户信息
|
||||||
|
const entranceList = ref([
|
||||||
|
{
|
||||||
|
icon: "collage",
|
||||||
|
label: "宝贝收藏",
|
||||||
|
path: "/home/Favorites"
|
||||||
},
|
},
|
||||||
data() {
|
{
|
||||||
return {
|
icon: "shop",
|
||||||
config,
|
label: "收藏店铺",
|
||||||
defaultAvatar,
|
path: "/home/Favorites?type=STORE"
|
||||||
userInfo: {}, // 用户信息
|
|
||||||
entranceList: [
|
|
||||||
{
|
|
||||||
icon: "collage",
|
|
||||||
label: "宝贝收藏",
|
|
||||||
path: "/home/Favorites"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: "shop",
|
|
||||||
label: "收藏店铺",
|
|
||||||
path: "/home/Favorites?type=STORE"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: "carts",
|
|
||||||
label: "购物车",
|
|
||||||
path: "/cart"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: "story",
|
|
||||||
label: "我的足迹",
|
|
||||||
path: "/home/MyTracks"
|
|
||||||
},
|
|
||||||
],
|
|
||||||
appendList: [
|
|
||||||
{
|
|
||||||
icon: "support",
|
|
||||||
label: "帮助中心",
|
|
||||||
path: "/article"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: "feedback",
|
|
||||||
label: "我的评论",
|
|
||||||
path: "/home/CommentList"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: "notice",
|
|
||||||
label: "收货地址",
|
|
||||||
path: "/home/MyAddress"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: "notification",
|
|
||||||
label: "我的消息",
|
|
||||||
path: "/home/MsgList"
|
|
||||||
},
|
|
||||||
],
|
|
||||||
recentList: [
|
|
||||||
{
|
|
||||||
value: "0",
|
|
||||||
label: "购物车"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "0",
|
|
||||||
label: "待收货"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "0",
|
|
||||||
label: "待发货"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "0",
|
|
||||||
label: "待付款"
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
methods: {
|
{
|
||||||
getIconUrl,
|
icon: "carts",
|
||||||
carouselItemKey(item, index) {
|
label: "购物车",
|
||||||
return item?.img || item?.url || `slide-${index}`;
|
path: "/cart"
|
||||||
},
|
},
|
||||||
resolveRoute(pathStr) {
|
{
|
||||||
if (!pathStr) return { path: "/" };
|
icon: "story",
|
||||||
const [path, search] = pathStr.split("?");
|
label: "我的足迹",
|
||||||
if (!search) return { path };
|
path: "/home/MyTracks"
|
||||||
return {
|
},
|
||||||
path,
|
]);
|
||||||
query: Object.fromEntries(new URLSearchParams(search)),
|
const appendList = ref([
|
||||||
};
|
{
|
||||||
},
|
icon: "support",
|
||||||
isMemberRoute(location) {
|
label: "帮助中心",
|
||||||
const path = location.path && location.path.charAt(0) === "/" ? location.path : `/${location.path || ""}`;
|
path: "/article"
|
||||||
return path === "/home" || path.startsWith("/home/") || path === "/cart";
|
},
|
||||||
},
|
{
|
||||||
normalizeLocation(location) {
|
icon: "feedback",
|
||||||
if (!location.path || location.path.charAt(0) === "/") return location;
|
label: "我的评论",
|
||||||
return { ...location, path: `/${location.path}` };
|
path: "/home/CommentList"
|
||||||
},
|
},
|
||||||
promptLoginThenNavigate(location) {
|
{
|
||||||
Modal.confirm({
|
icon: "notice",
|
||||||
title: "温馨提示",
|
label: "收货地址",
|
||||||
content: "请登录后执行此操作",
|
path: "/home/MyAddress"
|
||||||
okText: "立即登录",
|
},
|
||||||
cancelText: "取消",
|
{
|
||||||
onOk: () => {
|
icon: "notification",
|
||||||
this.$router.push({
|
label: "我的消息",
|
||||||
path: "/login",
|
path: "/home/MsgList"
|
||||||
query: {
|
},
|
||||||
rePath: location.path,
|
]);
|
||||||
query: JSON.stringify(location.query || {}),
|
const recentList = ref([
|
||||||
},
|
{
|
||||||
});
|
value: "0",
|
||||||
|
label: "购物车"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "0",
|
||||||
|
label: "待收货"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "0",
|
||||||
|
label: "待发货"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "0",
|
||||||
|
label: "待付款"
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
function carouselItemKey(item, index) {
|
||||||
|
return item?.img || item?.url || `slide-${index}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveRoute(pathStr) {
|
||||||
|
if (!pathStr) return { path: "/" };
|
||||||
|
const [path, search] = pathStr.split("?");
|
||||||
|
if (!search) return { path };
|
||||||
|
return {
|
||||||
|
path,
|
||||||
|
query: Object.fromEntries(new URLSearchParams(search)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function isMemberRoute(location) {
|
||||||
|
const path = location.path && location.path.charAt(0) === "/" ? location.path : `/${location.path || ""}`;
|
||||||
|
return path === "/home" || path.startsWith("/home/") || path === "/cart";
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeLocation(location) {
|
||||||
|
if (!location.path || location.path.charAt(0) === "/") return location;
|
||||||
|
return { ...location, path: `/${location.path}` };
|
||||||
|
}
|
||||||
|
|
||||||
|
function promptLoginThenNavigate(location) {
|
||||||
|
Modal.confirm({
|
||||||
|
title: "温馨提示",
|
||||||
|
content: "请登录后执行此操作",
|
||||||
|
okText: "立即登录",
|
||||||
|
cancelText: "取消",
|
||||||
|
onOk: () => {
|
||||||
|
router.push({
|
||||||
|
path: "/login",
|
||||||
|
query: {
|
||||||
|
rePath: location.path,
|
||||||
|
query: JSON.stringify(location.query || {}),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
goLogin() {
|
});
|
||||||
this.$router.push("/login");
|
}
|
||||||
},
|
|
||||||
// 快捷跳转中心
|
|
||||||
entryControl(val) {
|
|
||||||
const location = this.normalizeLocation(this.resolveRoute(val.path));
|
|
||||||
if (this.isMemberRoute(location) && !storage.getItem("userInfo")) {
|
|
||||||
this.promptLoginThenNavigate(location);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.$router.push(location);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
if (storage.getItem("userInfo"))
|
|
||||||
this.userInfo = JSON.parse(storage.getItem("userInfo"));
|
|
||||||
|
|
||||||
},
|
function goLogin() {
|
||||||
};
|
router.push("/login");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 快捷跳转中心
|
||||||
|
function entryControl(val) {
|
||||||
|
const location = normalizeLocation(resolveRoute(val.path));
|
||||||
|
if (isMemberRoute(location) && !storage.getItem("userInfo")) {
|
||||||
|
promptLoginThenNavigate(location);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
router.push(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (storage.getItem("userInfo"))
|
||||||
|
userInfo.value = JSON.parse(storage.getItem("userInfo"));
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -17,28 +17,26 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
import { onMounted, ref } from 'vue'
|
||||||
name: 'modelCarousel1',
|
|
||||||
props: ['data'],
|
defineOptions({ name: 'modelCarousel1' })
|
||||||
data () {
|
|
||||||
return {
|
const props = defineProps(['data'])
|
||||||
showModal: false, // modal显隐
|
|
||||||
selected: null, // 已选数据
|
const showModal = ref(false) // modal显隐
|
||||||
picModelFlag: false, // 选择图片modal
|
const selected = ref(null) // 已选数据
|
||||||
bgColor: '#fff' // 轮播背景色
|
const picModelFlag = ref(false) // 选择图片modal
|
||||||
};
|
const bgColor = ref('#fff') // 轮播背景色
|
||||||
},
|
|
||||||
mounted () {
|
onMounted(() => {
|
||||||
this.bgColor = this.data.options.list[0].bgColor
|
bgColor.value = props.data.options.list[0].bgColor
|
||||||
},
|
})
|
||||||
methods: {
|
|
||||||
// 自动切换时改变背景色
|
// 自动切换时改变背景色
|
||||||
autoChange (index) {
|
function autoChange(index) {
|
||||||
this.bgColor = this.data.options.list[index].bgColor
|
bgColor.value = props.data.options.list[index].bgColor
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -55,49 +55,48 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { onMounted, reactive, ref } from 'vue'
|
||||||
import {articleList} from '@/api/common.js'
|
import { useRouter } from 'vue-router'
|
||||||
|
import { articleList as getArticleListApi } from '@/api/common.js'
|
||||||
import storage from '@/plugins/storage';
|
import storage from '@/plugins/storage';
|
||||||
import { User } from '@element-plus/icons-vue';
|
import { User } from '@element-plus/icons-vue';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
export default {
|
|
||||||
components: { User },
|
defineOptions({ name: 'modelCarousel2' })
|
||||||
name: 'modelCarousel2',
|
|
||||||
props: ['data'],
|
defineProps(['data'])
|
||||||
data () {
|
|
||||||
return {
|
const router = useRouter()
|
||||||
config,
|
|
||||||
userInfo: {}, // 用户信息
|
const userInfo = ref({}) // 用户信息
|
||||||
articleList: [], // 常见问题
|
const articleList = ref([]) // 常见问题
|
||||||
params: { // 请求常见问题参数
|
const params = reactive({ // 请求常见问题参数
|
||||||
pageNumber: 1,
|
pageNumber: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
type: 'ANNOUNCEMENT',
|
type: 'ANNOUNCEMENT',
|
||||||
sort: 'sort'
|
sort: 'sort'
|
||||||
}
|
})
|
||||||
};
|
|
||||||
},
|
function getArticleList () { // 获取常见问题列表
|
||||||
methods: {
|
getArticleListApi(params).then(res => {
|
||||||
getArticleList () { // 获取常见问题列表
|
if (res.success) {
|
||||||
articleList(this.params).then(res => {
|
articleList.value = res.result.records
|
||||||
if (res.success) {
|
|
||||||
this.articleList = res.result.records
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
goArticle (id) { // 跳转文章详情
|
|
||||||
this.$router.push({
|
|
||||||
path: '/article',
|
|
||||||
query: {id}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
mounted () {
|
}
|
||||||
if (storage.getItem('userInfo')) this.userInfo = JSON.parse(storage.getItem('userInfo'));
|
|
||||||
this.getArticleList()
|
function goArticle (id) { // 跳转文章详情
|
||||||
}
|
router.push({
|
||||||
};
|
path: '/article',
|
||||||
|
query: {id}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (storage.getItem('userInfo')) userInfo.value = JSON.parse(storage.getItem('userInfo'));
|
||||||
|
getArticleList()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -19,20 +19,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
import { ref } from 'vue';
|
||||||
props: {
|
|
||||||
data: {
|
const props = defineProps({
|
||||||
type: Object,
|
data: {
|
||||||
default: null
|
type: Object,
|
||||||
}
|
default: null
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
options: this.data.options // 装修数据
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
|
const options = ref(props.data.options); // 装修数据
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.first-page-advert {
|
.first-page-advert {
|
||||||
|
|||||||
@@ -70,21 +70,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
import { ref } from 'vue'
|
||||||
props: {
|
|
||||||
data: {
|
const props = defineProps({
|
||||||
type: Object,
|
data: {
|
||||||
default: null
|
type: Object,
|
||||||
}
|
default: null
|
||||||
},
|
}
|
||||||
data () {
|
})
|
||||||
return {
|
|
||||||
options: this.data.options // 装修数据
|
const options = ref(props.data.options) // 装修数据
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {}
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.new-goods {
|
.new-goods {
|
||||||
|
|||||||
@@ -31,70 +31,80 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
import { onMounted, ref, watch } from 'vue';
|
||||||
mounted() {
|
import { useRouter } from 'vue-router';
|
||||||
window.addEventListener('scroll',this.handleScrollx,true)
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
},
|
},
|
||||||
props: {
|
});
|
||||||
data: {
|
|
||||||
type: Object,
|
const emit = defineEmits(['content']);
|
||||||
default: null,
|
|
||||||
},
|
const router = useRouter();
|
||||||
},
|
|
||||||
data() {
|
const obtain = ref(null);
|
||||||
return {
|
const screenHeight = ref(document.body.clientHeight);
|
||||||
screenHeight:document.body.clientHeight,
|
const scrollHieght = ref(0);
|
||||||
scrollHieght:0,
|
const topSearchShow = ref(true); //展示 导航条
|
||||||
topSearchShow: true, //展示 导航条
|
const topIndex = ref(0); // 当前滚动条位置 (取整)
|
||||||
topIndex: 0, // 当前滚动条位置 (取整)
|
const scrollTops = ref(0);
|
||||||
scrollTops: 0,
|
const open = ref('');
|
||||||
open:'',
|
const currentIndex = ref(0); // 当前分类下标
|
||||||
currentIndex: 0, // 当前分类下标
|
const conData = ref(props.data); // 装修数据
|
||||||
conData: this.data, // 装修数据
|
const showContent = ref(true); // 是否展示内容
|
||||||
showContent: true, // 是否展示内容
|
|
||||||
};
|
watch(
|
||||||
},
|
() => props.data,
|
||||||
watch: {
|
(val) => {
|
||||||
data: function (val) {
|
conData.value = val;
|
||||||
this.conData = val;
|
}
|
||||||
},
|
);
|
||||||
conData: function (val) {
|
|
||||||
this.$emit("content", val);
|
watch(conData, (val) => {
|
||||||
},
|
emit('content', val);
|
||||||
},
|
});
|
||||||
methods: {
|
|
||||||
handleScrollx(){
|
function handleScrollx() {
|
||||||
// console.log('滚动高度',window.pageYOffset) // 获取滚动条的高度
|
// console.log('滚动高度',window.pageYOffset) // 获取滚动条的高度
|
||||||
// console.log(this.$refs.obtain.getBoundingClientRect().top) //获取到距离顶部的距离
|
// console.log(obtain.value.getBoundingClientRect().top) //获取到距离顶部的距离
|
||||||
// this.scrollHieght = Number(window.pageYOffset);//获取到距离顶部的距离
|
// scrollHieght.value = Number(window.pageYOffset);//获取到距离顶部的距离
|
||||||
// this.scrollTops = Number(this.$refs.obtain.getBoundingClientRect().top); // 获取到距离顶部的距离
|
// scrollTops.value = Number(obtain.value.getBoundingClientRect().top); // 获取到距离顶部的距离
|
||||||
// this.topSearchShow = true; // 展示图钉
|
// topSearchShow.value = true; // 展示图钉
|
||||||
// if(this.scrollTops < -660){ // 超过隐藏
|
// if(scrollTops.value < -660){ // 超过隐藏
|
||||||
// this.topSearchShow = false;
|
// topSearchShow.value = false;
|
||||||
// }
|
// }
|
||||||
},
|
}
|
||||||
toguid(path,id){
|
|
||||||
var path =path;
|
function toguid(path, id) {
|
||||||
var Id = id;
|
var path = path;
|
||||||
localStorage.setItem('toId',Id);
|
var Id = id;
|
||||||
this.$router.push(path);
|
localStorage.setItem('toId', Id);
|
||||||
},
|
router.push(path);
|
||||||
change(status){ //获取是否获取到图钉
|
}
|
||||||
this.open = status
|
|
||||||
},
|
function change(status) { //获取是否获取到图钉
|
||||||
gotoDemo(){ // 跳转到demo的位置
|
open.value = status
|
||||||
if(this.open){ // 获取到图钉之后在跳转当前位置
|
}
|
||||||
document.querySelector("#demo").scrollIntoView(true);
|
|
||||||
}
|
function gotoDemo() { // 跳转到demo的位置
|
||||||
//scrollIntoView()可以在所有的HTML元素上调用,通过滚动浏览器窗口或某个容器元素
|
if (open.value) { // 获取到图钉之后在跳转当前位置
|
||||||
},
|
document.querySelector("#demo").scrollIntoView(true);
|
||||||
changeCurr(index) {
|
}
|
||||||
// 选择分类
|
//scrollIntoView()可以在所有的HTML元素上调用,通过滚动浏览器窗口或某个容器元素
|
||||||
this.currentIndex = index;
|
}
|
||||||
},
|
|
||||||
},
|
function changeCurr(index) {
|
||||||
};
|
// 选择分类
|
||||||
|
currentIndex.value = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('scroll', handleScrollx, true)
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.not-enough {
|
.not-enough {
|
||||||
|
|||||||
@@ -58,22 +58,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
import { ref } from 'vue';
|
||||||
props: {
|
|
||||||
data: {
|
const props = defineProps({
|
||||||
type: Object,
|
data: {
|
||||||
default: () => ({})
|
type: Object,
|
||||||
}
|
default: () => ({})
|
||||||
},
|
}
|
||||||
data () {
|
});
|
||||||
return {
|
|
||||||
msgLeft: this.data.options.contentLeft, // 左侧数据
|
const msgLeft = ref(props.data.options.contentLeft); // 左侧数据
|
||||||
msgRight: this.data.options.contentRight // 右侧数据
|
const msgRight = ref(props.data.options.contentRight); // 右侧数据
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {}
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.recommend {
|
.recommend {
|
||||||
|
|||||||
@@ -29,94 +29,97 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
import { onBeforeUnmount, onMounted, ref, watch } from "vue";
|
||||||
props: {
|
import { useRouter } from "vue-router";
|
||||||
data: Object,
|
|
||||||
},
|
const props = defineProps({
|
||||||
data() {
|
data: Object,
|
||||||
return {
|
});
|
||||||
list: [],
|
|
||||||
goodsList: [],
|
const router = useRouter();
|
||||||
actStatus: 0,
|
|
||||||
actName: "限时秒杀",
|
const list = ref([]);
|
||||||
currIndex: 0,
|
const goodsList = ref([]);
|
||||||
currHour: "00",
|
const actStatus = ref(0);
|
||||||
diffSeconds: 0,
|
const actName = ref("限时秒杀");
|
||||||
hours: 0,
|
const currIndex = ref(0);
|
||||||
minutes: 0,
|
const currHour = ref("00");
|
||||||
seconds: 0,
|
const diffSeconds = ref(0);
|
||||||
interval: null,
|
const hours = ref(0);
|
||||||
};
|
const minutes = ref(0);
|
||||||
},
|
const seconds = ref(0);
|
||||||
watch: {
|
const interval = ref(null);
|
||||||
currIndex(val) {
|
|
||||||
clearInterval(this.interval);
|
watch(currIndex, (val) => {
|
||||||
this.interval = null;
|
clearInterval(interval.value);
|
||||||
this.countDown(val);
|
interval.value = null;
|
||||||
this.goodsList = this.list[val]?.seckillGoodsList || [];
|
countDown(val);
|
||||||
},
|
goodsList.value = list.value[val]?.seckillGoodsList || [];
|
||||||
diffSeconds(val) {
|
});
|
||||||
const hours = Math.floor(val / 3600);
|
|
||||||
const minutes = Math.floor(val / 60) % 60;
|
watch(diffSeconds, (val) => {
|
||||||
const seconds = val % 60;
|
const h = Math.floor(val / 3600);
|
||||||
this.hours = filteTime(hours);
|
const m = Math.floor(val / 60) % 60;
|
||||||
this.minutes = filteTime(minutes);
|
const s = val % 60;
|
||||||
this.seconds = filteTime(seconds);
|
hours.value = filteTime(h);
|
||||||
if (val <= 0) {
|
minutes.value = filteTime(m);
|
||||||
clearInterval(this.interval);
|
seconds.value = filteTime(s);
|
||||||
this.interval = null;
|
if (val <= 0) {
|
||||||
}
|
clearInterval(interval.value);
|
||||||
function filteTime(time) {
|
interval.value = null;
|
||||||
return time < 10 ? "0" + time : time;
|
}
|
||||||
}
|
function filteTime(time) {
|
||||||
},
|
return time < 10 ? "0" + time : time;
|
||||||
},
|
}
|
||||||
beforeUnmount() {
|
});
|
||||||
clearInterval(this.interval);
|
|
||||||
},
|
onBeforeUnmount(() => {
|
||||||
mounted() {
|
clearInterval(interval.value);
|
||||||
this.getListByDay();
|
});
|
||||||
},
|
|
||||||
methods: {
|
onMounted(() => {
|
||||||
goToSeckill() {
|
getListByDay();
|
||||||
this.$router.push("/seckill");
|
});
|
||||||
},
|
|
||||||
countDown(currIndex) {
|
function goToSeckill() {
|
||||||
const zeroTime = new Date(new Date().toLocaleDateString()).getTime();
|
router.push("/seckill");
|
||||||
const currTime = new Date().getTime();
|
}
|
||||||
let actTime = 0;
|
|
||||||
const nowHour = new Date().getHours();
|
function countDown(index) {
|
||||||
if (this.list[currIndex].timeLine > nowHour) {
|
const zeroTime = new Date(new Date().toLocaleDateString()).getTime();
|
||||||
this.actStatus = 0;
|
const currTime = new Date().getTime();
|
||||||
actTime = zeroTime + this.list[currIndex].timeLine * 3600 * 1000;
|
let actTime = 0;
|
||||||
} else if (this.list[currIndex].timeLine <= nowHour) {
|
const nowHour = new Date().getHours();
|
||||||
this.actStatus = 1;
|
if (list.value[index].timeLine > nowHour) {
|
||||||
if (currIndex === this.list.length - 1) {
|
actStatus.value = 0;
|
||||||
actTime = zeroTime + 24 * 3600 * 1000;
|
actTime = zeroTime + list.value[index].timeLine * 3600 * 1000;
|
||||||
} else {
|
} else if (list.value[index].timeLine <= nowHour) {
|
||||||
actTime = zeroTime + this.list[currIndex + 1].timeLine * 3600 * 1000;
|
actStatus.value = 1;
|
||||||
}
|
if (index === list.value.length - 1) {
|
||||||
}
|
actTime = zeroTime + 24 * 3600 * 1000;
|
||||||
this.currHour = this.list[this.currIndex].timeLine;
|
} else {
|
||||||
this.diffSeconds = Math.floor((actTime - currTime) / 1000);
|
actTime = zeroTime + list.value[index + 1].timeLine * 3600 * 1000;
|
||||||
this.interval = setInterval(() => {
|
}
|
||||||
this.diffSeconds--;
|
}
|
||||||
}, 1000);
|
currHour.value = list.value[currIndex.value].timeLine;
|
||||||
},
|
diffSeconds.value = Math.floor((actTime - currTime) / 1000);
|
||||||
getListByDay() {
|
interval.value = setInterval(() => {
|
||||||
this.list = this.data?.options?.list || [];
|
diffSeconds.value--;
|
||||||
if (!this.list.length) {
|
}, 1000);
|
||||||
this.goodsList = [];
|
}
|
||||||
return;
|
|
||||||
}
|
function getListByDay() {
|
||||||
this.goodsList = this.list[0]?.seckillGoodsList || [];
|
list.value = props.data?.options?.list || [];
|
||||||
if (this.goodsList.length) {
|
if (!list.value.length) {
|
||||||
this.countDown(this.currIndex);
|
goodsList.value = [];
|
||||||
}
|
return;
|
||||||
},
|
}
|
||||||
},
|
goodsList.value = list.value[0]?.seckillGoodsList || [];
|
||||||
};
|
if (goodsList.value.length) {
|
||||||
|
countDown(currIndex.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.seckill {
|
.seckill {
|
||||||
|
|||||||
@@ -44,24 +44,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
import { ref } from 'vue'
|
||||||
name: "forYour",
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
current: "",
|
|
||||||
};
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
data: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {},
|
defineOptions({ name: 'forYour' })
|
||||||
methods: {},
|
|
||||||
};
|
defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const current = ref('')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -43,31 +43,28 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
import { onMounted, ref } from "vue";
|
||||||
name: "goods",
|
|
||||||
props: {
|
defineOptions({ name: "goods" });
|
||||||
paramData: {
|
|
||||||
type: Object,
|
const props = defineProps({
|
||||||
default: () => ({}),
|
paramData: {
|
||||||
},
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
data() {
|
});
|
||||||
return {
|
|
||||||
current: "",
|
const current = ref("");
|
||||||
};
|
|
||||||
},
|
onMounted(() => {
|
||||||
watch: {},
|
current.value = props.paramData.options.labels[0].___index;
|
||||||
mounted() {
|
});
|
||||||
this.current = this.paramData.options.labels[0].___index;
|
|
||||||
},
|
// 筛选出当前分类下的商品
|
||||||
methods: {
|
function handleClickLabel(val, index) {
|
||||||
// 筛选出当前分类下的商品
|
current.value = index;
|
||||||
handleClickLabel(val, index) {
|
}
|
||||||
this.current = index;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -11,29 +11,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import left from './mixs/mix-goods'
|
import left from './mixs/mix-goods'
|
||||||
import right from './mixs/mix-brand'
|
import right from './mixs/mix-brand'
|
||||||
export default {
|
|
||||||
name: "mixModel",
|
|
||||||
data() {
|
|
||||||
return {}
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
data: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
left,
|
|
||||||
right
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
|
|
||||||
},
|
defineOptions({ name: "mixModel" });
|
||||||
methods: {}
|
|
||||||
}
|
defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -25,19 +25,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import { ArrowRight } from "@element-plus/icons-vue";
|
import { ArrowRight } from "@element-plus/icons-vue";
|
||||||
|
|
||||||
export default {
|
defineOptions({ name: "mix-brand" });
|
||||||
name: "mix-brand",
|
|
||||||
components: { ArrowRight },
|
defineProps({
|
||||||
props: {
|
data: {
|
||||||
data: {
|
type: Object,
|
||||||
type: Object,
|
default: () => ({}),
|
||||||
default: () => ({}),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -29,16 +29,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
defineOptions({ name: "mix-goods" });
|
||||||
name: "mix-goods",
|
|
||||||
props: {
|
defineProps({
|
||||||
data: {
|
data: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -11,22 +11,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
defineOptions({ name: "oneRowThreeColumns" });
|
||||||
name: "oneRowThreeColumns",
|
|
||||||
props: {
|
|
||||||
data: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
|
|
||||||
};
|
defineProps({
|
||||||
},
|
data: {
|
||||||
methods: {}
|
type: Object,
|
||||||
}
|
default: () => ({})
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -21,35 +21,32 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
import { openLink } from "@/utils/buyerLogin";
|
||||||
|
|
||||||
export default {
|
defineOptions({ name: "onlyGoodsList" });
|
||||||
name: "onlyGoodsList",
|
|
||||||
props: {
|
|
||||||
data: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
flag:false,
|
|
||||||
tabIndex:0,
|
|
||||||
current: 0,
|
|
||||||
showModal: false,
|
|
||||||
selected: {}, // 已选数据
|
|
||||||
picModelFlag: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
mounted(){
|
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
methods: {
|
});
|
||||||
handleClick(val){
|
|
||||||
this.linkTo(val.url)
|
const router = useRouter();
|
||||||
},
|
|
||||||
},
|
const flag = ref(false);
|
||||||
};
|
const tabIndex = ref(0);
|
||||||
|
const current = ref(0);
|
||||||
|
const showModal = ref(false);
|
||||||
|
const selected = ref({}); // 已选数据
|
||||||
|
const picModelFlag = ref(false);
|
||||||
|
|
||||||
|
function handleClick(val) {
|
||||||
|
openLink(val.url, router);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<span>发票信息</span>
|
<span>发票信息</span>
|
||||||
</p></template>
|
</p></template>
|
||||||
<div class="nav-content">
|
<div class="nav-content">
|
||||||
<el-form :model="invoiceForm" ref="form" label-position="left" :rules="formRules" label-width="118px">
|
<el-form :model="invoiceForm" ref="formRef" label-position="left" :rules="formRules" label-width="118px">
|
||||||
<el-form-item label="发票类型">
|
<el-form-item label="发票类型">
|
||||||
<el-radio-group v-model="invoice" type="button" button-style="solid" @change="onInvoiceKindChange">
|
<el-radio-group v-model="invoice" type="button" button-style="solid" @change="onInvoiceKindChange">
|
||||||
<el-radio :label="1">电子普通发票</el-radio>
|
<el-radio :label="1">电子普通发票</el-radio>
|
||||||
@@ -86,210 +86,209 @@
|
|||||||
</div></el-dialog>
|
</div></el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
|
import { computed, nextTick, ref, watch } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
import { receiptSelect } from '@/api/cart.js';
|
import { receiptSelect } from '@/api/cart.js';
|
||||||
import { mobile, email } from '@/plugins/RegExp.js';
|
import { mobile, email } from '@/plugins/RegExp.js';
|
||||||
|
|
||||||
export default {
|
defineOptions({ name: 'invoiceModal' });
|
||||||
name: 'invoiceModal',
|
|
||||||
emits: ['change'],
|
const props = defineProps(['invoiceData']);
|
||||||
data () {
|
|
||||||
|
const emit = defineEmits(['change']);
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const invoice = ref(1); // 1 电子普通发票 2 增值税专用发票
|
||||||
|
const invoiceAvailable = ref(false);
|
||||||
|
const loading = ref(false);
|
||||||
|
const invoiceForm = ref({
|
||||||
|
receiptTitle: '',
|
||||||
|
personalName: '',
|
||||||
|
companyName: '',
|
||||||
|
taxpayerId: '',
|
||||||
|
receiptContent: '商品明细',
|
||||||
|
receiptPhone: '',
|
||||||
|
receiptEmail: '',
|
||||||
|
companyAddress: '',
|
||||||
|
companyPhone: '',
|
||||||
|
bankName: '',
|
||||||
|
bankAccount: ''
|
||||||
|
});
|
||||||
|
const type = ref(1);
|
||||||
|
|
||||||
|
const formRef = ref(null);
|
||||||
|
|
||||||
|
const formRules = computed(() => {
|
||||||
|
const receiptPhoneRules = [
|
||||||
|
{ required: true, message: '请填写收票人手机', trigger: 'blur' },
|
||||||
|
{ pattern: mobile, message: '请填写正确的手机号码', trigger: 'blur' }
|
||||||
|
];
|
||||||
|
// 收票人邮箱可选:仅校验格式;提示文案须与手机区分,避免误用「请填写收票人手机」
|
||||||
|
const receiptEmailRules = [
|
||||||
|
{
|
||||||
|
validator (rule, val, cb) {
|
||||||
|
const s = val != null ? String(val).trim() : '';
|
||||||
|
if (!s) {
|
||||||
|
cb();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!email.test(s)) {
|
||||||
|
cb(new Error('请填写正确的收票人邮箱'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cb();
|
||||||
|
},
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
if (invoice.value === 2) {
|
||||||
return {
|
return {
|
||||||
invoice: 1, // 1 电子普通发票 2 增值税专用发票
|
companyName: [{ required: true, message: '请填写单位名称', trigger: 'blur' }],
|
||||||
invoiceAvailable: false,
|
taxpayerId: [{ required: true, message: '请填写纳税人识别号', trigger: 'blur' }],
|
||||||
loading: false,
|
companyAddress: [{ required: true, message: '请填写单位地址', trigger: 'blur' }],
|
||||||
invoiceForm: {
|
companyPhone: [{ required: true, message: '请填写单位电话', trigger: 'blur' }],
|
||||||
receiptTitle: '',
|
bankName: [{ required: true, message: '请填写开户银行', trigger: 'blur' }],
|
||||||
personalName: '',
|
bankAccount: [{ required: true, message: '请填写银行账号', trigger: 'blur' }],
|
||||||
companyName: '',
|
receiptPhone: receiptPhoneRules,
|
||||||
taxpayerId: '',
|
receiptEmail: receiptEmailRules
|
||||||
receiptContent: '商品明细',
|
|
||||||
receiptPhone: '',
|
|
||||||
receiptEmail: '',
|
|
||||||
companyAddress: '',
|
|
||||||
companyPhone: '',
|
|
||||||
bankName: '',
|
|
||||||
bankAccount: ''
|
|
||||||
},
|
|
||||||
type: 1
|
|
||||||
};
|
};
|
||||||
},
|
|
||||||
props: ['invoiceData'],
|
|
||||||
computed: {
|
|
||||||
formRules () {
|
|
||||||
const receiptPhoneRules = [
|
|
||||||
{ required: true, message: '请填写收票人手机', trigger: 'blur' },
|
|
||||||
{ pattern: mobile, message: '请填写正确的手机号码', trigger: 'blur' }
|
|
||||||
];
|
|
||||||
// 收票人邮箱可选:仅校验格式;提示文案须与手机区分,避免误用「请填写收票人手机」
|
|
||||||
const receiptEmailRules = [
|
|
||||||
{
|
|
||||||
validator (rule, val, cb) {
|
|
||||||
const s = val != null ? String(val).trim() : '';
|
|
||||||
if (!s) {
|
|
||||||
cb();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!email.test(s)) {
|
|
||||||
cb(new Error('请填写正确的收票人邮箱'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cb();
|
|
||||||
},
|
|
||||||
trigger: 'blur'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
if (this.invoice === 2) {
|
|
||||||
return {
|
|
||||||
companyName: [{ required: true, message: '请填写单位名称', trigger: 'blur' }],
|
|
||||||
taxpayerId: [{ required: true, message: '请填写纳税人识别号', trigger: 'blur' }],
|
|
||||||
companyAddress: [{ required: true, message: '请填写单位地址', trigger: 'blur' }],
|
|
||||||
companyPhone: [{ required: true, message: '请填写单位电话', trigger: 'blur' }],
|
|
||||||
bankName: [{ required: true, message: '请填写开户银行', trigger: 'blur' }],
|
|
||||||
bankAccount: [{ required: true, message: '请填写银行账号', trigger: 'blur' }],
|
|
||||||
receiptPhone: receiptPhoneRules,
|
|
||||||
receiptEmail: receiptEmailRules
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const rules = {
|
|
||||||
receiptPhone: receiptPhoneRules,
|
|
||||||
receiptEmail: receiptEmailRules
|
|
||||||
};
|
|
||||||
if (this.type === 1) {
|
|
||||||
rules.personalName = [{ required: true, message: '请填写个人名称', trigger: 'blur' }];
|
|
||||||
} else {
|
|
||||||
rules.companyName = [{ required: true, message: '请填写单位名称', trigger: 'blur' }];
|
|
||||||
rules.taxpayerId = [{ required: true, message: '请填写纳税人识别号', trigger: 'blur' }];
|
|
||||||
}
|
|
||||||
return rules;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
invoiceData: {
|
|
||||||
handler (val) {
|
|
||||||
if (!val) return;
|
|
||||||
this.invoiceForm = { ...this.invoiceForm, ...val };
|
|
||||||
if (!this.invoiceForm.receiptPhone) this.invoiceForm.receiptPhone = '';
|
|
||||||
if (!this.invoiceForm.receiptEmail) this.invoiceForm.receiptEmail = '';
|
|
||||||
if (!this.invoiceForm.receiptContent) this.invoiceForm.receiptContent = '商品明细';
|
|
||||||
if (!this.invoiceForm.companyAddress) this.invoiceForm.companyAddress = '';
|
|
||||||
if (!this.invoiceForm.companyPhone) this.invoiceForm.companyPhone = '';
|
|
||||||
if (!this.invoiceForm.bankName) this.invoiceForm.bankName = '';
|
|
||||||
if (!this.invoiceForm.bankAccount) this.invoiceForm.bankAccount = '';
|
|
||||||
if (!this.invoiceForm.personalName) this.invoiceForm.personalName = '';
|
|
||||||
if (!this.invoiceForm.companyName) this.invoiceForm.companyName = '';
|
|
||||||
|
|
||||||
const rt = val.receiptType;
|
|
||||||
if (rt === 2 || rt === '2' || val.invoiceKind === 'VAT_SPECIAL') {
|
|
||||||
this.invoice = 2;
|
|
||||||
this.type = 2;
|
|
||||||
} else {
|
|
||||||
this.invoice = 1;
|
|
||||||
if (val.taxpayerId) this.type = 2;
|
|
||||||
else this.type = 1;
|
|
||||||
}
|
|
||||||
// 兼容后端 receiptTitle 存「个人/单位」,实际名称在 personalName/companyName;旧数据名称可能只在 receiptTitle
|
|
||||||
const titleAsName = (t) => (t && t !== '个人' && t !== '单位' ? t : '');
|
|
||||||
if (this.invoice === 2) {
|
|
||||||
this.invoiceForm.companyName = val.companyName || titleAsName(val.receiptTitle) || '';
|
|
||||||
} else if (this.type === 2) {
|
|
||||||
this.invoiceForm.companyName = val.companyName || titleAsName(val.receiptTitle) || '';
|
|
||||||
this.invoiceForm.personalName = val.personalName || '';
|
|
||||||
} else {
|
|
||||||
this.invoiceForm.personalName = val.personalName || titleAsName(val.receiptTitle) || '';
|
|
||||||
this.invoiceForm.companyName = val.companyName || '';
|
|
||||||
}
|
|
||||||
if (this.invoice === 1) {
|
|
||||||
this.invoiceForm.companyAddress = '';
|
|
||||||
this.invoiceForm.companyPhone = '';
|
|
||||||
this.invoiceForm.bankName = '';
|
|
||||||
this.invoiceForm.bankAccount = '';
|
|
||||||
}
|
|
||||||
if (this.invoice === 2) {
|
|
||||||
this.invoiceForm.personalName = '';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
deep: true,
|
|
||||||
immediate: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
onHeaderTypeChange (val) {
|
|
||||||
if (this.invoice === 1 && val === 1 && this.invoiceForm.personalName === '个人') {
|
|
||||||
this.invoiceForm.personalName = '';
|
|
||||||
}
|
|
||||||
this.$nextTick(() => {
|
|
||||||
if (this.$refs.form) this.$refs.form.clearValidate();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onInvoiceKindChange (val) {
|
|
||||||
this.type = val === 2 ? 2 : 1;
|
|
||||||
this.invoiceForm = {
|
|
||||||
receiptTitle: '',
|
|
||||||
personalName: '',
|
|
||||||
companyName: '',
|
|
||||||
taxpayerId: '',
|
|
||||||
receiptContent: '商品明细',
|
|
||||||
receiptPhone: '',
|
|
||||||
receiptEmail: '',
|
|
||||||
companyAddress: '',
|
|
||||||
companyPhone: '',
|
|
||||||
bankName: '',
|
|
||||||
bankAccount: ''
|
|
||||||
};
|
|
||||||
this.$nextTick(() => {
|
|
||||||
if (this.$refs.form) this.$refs.form.clearValidate();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
async save () {
|
|
||||||
if (this.invoice === 2) {
|
|
||||||
this.invoiceForm.receiptContent = '商品明细';
|
|
||||||
}
|
|
||||||
|
|
||||||
return await new Promise(resolve => {
|
|
||||||
this.$refs.form.validate(valid => resolve(valid));
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
async submit () {
|
|
||||||
if (!await this.save()) return;
|
|
||||||
this.loading = true;
|
|
||||||
const raw = JSON.parse(JSON.stringify(this.invoiceForm));
|
|
||||||
if (this.invoice === 1) {
|
|
||||||
delete raw.companyAddress;
|
|
||||||
delete raw.companyPhone;
|
|
||||||
delete raw.bankName;
|
|
||||||
delete raw.bankAccount;
|
|
||||||
delete raw.invoiceAddress;
|
|
||||||
if (this.type === 1) {
|
|
||||||
delete raw.taxpayerId;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
delete raw.personalName;
|
|
||||||
}
|
|
||||||
const isCompanyTitle = this.invoice === 2 || this.type === 2;
|
|
||||||
const titleName = isCompanyTitle
|
|
||||||
? (raw.companyName != null ? String(raw.companyName).trim() : '')
|
|
||||||
: (raw.personalName != null ? String(raw.personalName).trim() : '');
|
|
||||||
const submit = {
|
|
||||||
way: this.$route.query.way,
|
|
||||||
...raw,
|
|
||||||
receiptType: this.invoice,
|
|
||||||
receiptTitle: isCompanyTitle ? '单位' : '个人',
|
|
||||||
personalName: isCompanyTitle ? '' : titleName,
|
|
||||||
companyName: isCompanyTitle ? titleName : ''
|
|
||||||
};
|
|
||||||
let receipt = await receiptSelect(submit);
|
|
||||||
if (receipt.success) {
|
|
||||||
this.$emit('change', true);
|
|
||||||
}
|
|
||||||
this.loading = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
const rules = {
|
||||||
|
receiptPhone: receiptPhoneRules,
|
||||||
|
receiptEmail: receiptEmailRules
|
||||||
|
};
|
||||||
|
if (type.value === 1) {
|
||||||
|
rules.personalName = [{ required: true, message: '请填写个人名称', trigger: 'blur' }];
|
||||||
|
} else {
|
||||||
|
rules.companyName = [{ required: true, message: '请填写单位名称', trigger: 'blur' }];
|
||||||
|
rules.taxpayerId = [{ required: true, message: '请填写纳税人识别号', trigger: 'blur' }];
|
||||||
|
}
|
||||||
|
return rules;
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => props.invoiceData, (val) => {
|
||||||
|
if (!val) return;
|
||||||
|
invoiceForm.value = { ...invoiceForm.value, ...val };
|
||||||
|
if (!invoiceForm.value.receiptPhone) invoiceForm.value.receiptPhone = '';
|
||||||
|
if (!invoiceForm.value.receiptEmail) invoiceForm.value.receiptEmail = '';
|
||||||
|
if (!invoiceForm.value.receiptContent) invoiceForm.value.receiptContent = '商品明细';
|
||||||
|
if (!invoiceForm.value.companyAddress) invoiceForm.value.companyAddress = '';
|
||||||
|
if (!invoiceForm.value.companyPhone) invoiceForm.value.companyPhone = '';
|
||||||
|
if (!invoiceForm.value.bankName) invoiceForm.value.bankName = '';
|
||||||
|
if (!invoiceForm.value.bankAccount) invoiceForm.value.bankAccount = '';
|
||||||
|
if (!invoiceForm.value.personalName) invoiceForm.value.personalName = '';
|
||||||
|
if (!invoiceForm.value.companyName) invoiceForm.value.companyName = '';
|
||||||
|
|
||||||
|
const rt = val.receiptType;
|
||||||
|
if (rt === 2 || rt === '2' || val.invoiceKind === 'VAT_SPECIAL') {
|
||||||
|
invoice.value = 2;
|
||||||
|
type.value = 2;
|
||||||
|
} else {
|
||||||
|
invoice.value = 1;
|
||||||
|
if (val.taxpayerId) type.value = 2;
|
||||||
|
else type.value = 1;
|
||||||
|
}
|
||||||
|
// 兼容后端 receiptTitle 存「个人/单位」,实际名称在 personalName/companyName;旧数据名称可能只在 receiptTitle
|
||||||
|
const titleAsName = (t) => (t && t !== '个人' && t !== '单位' ? t : '');
|
||||||
|
if (invoice.value === 2) {
|
||||||
|
invoiceForm.value.companyName = val.companyName || titleAsName(val.receiptTitle) || '';
|
||||||
|
} else if (type.value === 2) {
|
||||||
|
invoiceForm.value.companyName = val.companyName || titleAsName(val.receiptTitle) || '';
|
||||||
|
invoiceForm.value.personalName = val.personalName || '';
|
||||||
|
} else {
|
||||||
|
invoiceForm.value.personalName = val.personalName || titleAsName(val.receiptTitle) || '';
|
||||||
|
invoiceForm.value.companyName = val.companyName || '';
|
||||||
|
}
|
||||||
|
if (invoice.value === 1) {
|
||||||
|
invoiceForm.value.companyAddress = '';
|
||||||
|
invoiceForm.value.companyPhone = '';
|
||||||
|
invoiceForm.value.bankName = '';
|
||||||
|
invoiceForm.value.bankAccount = '';
|
||||||
|
}
|
||||||
|
if (invoice.value === 2) {
|
||||||
|
invoiceForm.value.personalName = '';
|
||||||
|
}
|
||||||
|
}, { deep: true, immediate: true });
|
||||||
|
|
||||||
|
function onHeaderTypeChange (val) {
|
||||||
|
if (invoice.value === 1 && val === 1 && invoiceForm.value.personalName === '个人') {
|
||||||
|
invoiceForm.value.personalName = '';
|
||||||
|
}
|
||||||
|
nextTick(() => {
|
||||||
|
if (formRef.value) formRef.value.clearValidate();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onInvoiceKindChange (val) {
|
||||||
|
type.value = val === 2 ? 2 : 1;
|
||||||
|
invoiceForm.value = {
|
||||||
|
receiptTitle: '',
|
||||||
|
personalName: '',
|
||||||
|
companyName: '',
|
||||||
|
taxpayerId: '',
|
||||||
|
receiptContent: '商品明细',
|
||||||
|
receiptPhone: '',
|
||||||
|
receiptEmail: '',
|
||||||
|
companyAddress: '',
|
||||||
|
companyPhone: '',
|
||||||
|
bankName: '',
|
||||||
|
bankAccount: ''
|
||||||
|
};
|
||||||
|
nextTick(() => {
|
||||||
|
if (formRef.value) formRef.value.clearValidate();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function save () {
|
||||||
|
if (invoice.value === 2) {
|
||||||
|
invoiceForm.value.receiptContent = '商品明细';
|
||||||
|
}
|
||||||
|
|
||||||
|
return await new Promise(resolve => {
|
||||||
|
formRef.value.validate(valid => resolve(valid));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submit () {
|
||||||
|
if (!await save()) return;
|
||||||
|
loading.value = true;
|
||||||
|
const raw = JSON.parse(JSON.stringify(invoiceForm.value));
|
||||||
|
if (invoice.value === 1) {
|
||||||
|
delete raw.companyAddress;
|
||||||
|
delete raw.companyPhone;
|
||||||
|
delete raw.bankName;
|
||||||
|
delete raw.bankAccount;
|
||||||
|
delete raw.invoiceAddress;
|
||||||
|
if (type.value === 1) {
|
||||||
|
delete raw.taxpayerId;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
delete raw.personalName;
|
||||||
|
}
|
||||||
|
const isCompanyTitle = invoice.value === 2 || type.value === 2;
|
||||||
|
const titleName = isCompanyTitle
|
||||||
|
? (raw.companyName != null ? String(raw.companyName).trim() : '')
|
||||||
|
: (raw.personalName != null ? String(raw.personalName).trim() : '');
|
||||||
|
const submit = {
|
||||||
|
way: route.query.way,
|
||||||
|
...raw,
|
||||||
|
receiptType: invoice.value,
|
||||||
|
receiptTitle: isCompanyTitle ? '单位' : '个人',
|
||||||
|
personalName: isCompanyTitle ? '' : titleName,
|
||||||
|
companyName: isCompanyTitle ? titleName : ''
|
||||||
|
};
|
||||||
|
let receipt = await receiptSelect(submit);
|
||||||
|
if (receipt.success) {
|
||||||
|
emit('change', true);
|
||||||
|
}
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ invoiceAvailable });
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.inv-type {
|
.inv-type {
|
||||||
|
|||||||
@@ -18,135 +18,132 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
|
import { onMounted, ref, watch } from "vue";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
import AMapLoader from "@amap/amap-jsapi-loader";
|
import AMapLoader from "@amap/amap-jsapi-loader";
|
||||||
import { handleRegion } from "@/api/address.js";
|
import { handleRegion } from "@/api/address.js";
|
||||||
import config from "@/config/index";
|
import config from "@/config/index";
|
||||||
|
|
||||||
export default {
|
defineOptions({ name: "map" });
|
||||||
name: "map",
|
|
||||||
data() {
|
const emit = defineEmits(["getAddress"]);
|
||||||
return {
|
|
||||||
config,
|
const showMap = ref(false); // 地图显隐
|
||||||
showMap: false, // 地图显隐
|
const mapSearch = ref(""); // 地图搜索
|
||||||
mapSearch: "", // 地图搜索
|
let map = null; // 初始化地图
|
||||||
map: null, // 初始化地图
|
let autoComplete = null; // 初始化搜索方法
|
||||||
autoComplete: null, // 初始化搜索方法
|
let geocoder = null; // 初始化地理、坐标转化
|
||||||
geocoder: null, // 初始化地理、坐标转化
|
let positionPicker = null; // 地图拖拽选点
|
||||||
positionPicker: null, // 地图拖拽选点
|
const tips = ref([]); //搜索关键字列表
|
||||||
tips: [], //搜索关键字列表
|
const addrContent = ref({}); // 回显地址信息
|
||||||
addrContent: {}, // 回显地址信息
|
const loading = ref(false); // 加载状态
|
||||||
loading: false, // 加载状态
|
|
||||||
|
watch(mapSearch, function (val) {
|
||||||
|
searchOfMap(val);
|
||||||
|
});
|
||||||
|
|
||||||
|
function ok() {
|
||||||
|
|
||||||
|
if (addrContent.value && addrContent.value.regeocode) {
|
||||||
|
const params = {
|
||||||
|
cityCode: addrContent.value.regeocode.addressComponent.citycode,
|
||||||
|
townName: addrContent.value.regeocode.addressComponent.township,
|
||||||
};
|
};
|
||||||
},
|
handleRegion(params).then((res) => {
|
||||||
watch: {
|
if (res.success) {
|
||||||
mapSearch: function (val) {
|
addrContent.value.addr = res.result.name.replace(/,/g, " ");
|
||||||
this.searchOfMap(val);
|
addrContent.value.addrId = res.result.id;
|
||||||
},
|
loading.value = false;
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
ok() {
|
|
||||||
|
|
||||||
if (this.addrContent && this.addrContent.regeocode) {
|
emit("getAddress", addrContent.value);
|
||||||
const params = {
|
|
||||||
cityCode: this.addrContent.regeocode.addressComponent.citycode,
|
|
||||||
townName: this.addrContent.regeocode.addressComponent.township,
|
|
||||||
};
|
|
||||||
handleRegion(params).then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
this.addrContent.addr = res.result.name.replace(/,/g, " ");
|
|
||||||
this.addrContent.addrId = res.result.id;
|
|
||||||
this.loading = false;
|
|
||||||
|
|
||||||
this.$emit("getAddress", this.addrContent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
Message.error('未获取到坐标信息!请查看高德API配置是否正确')
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Message.error('未获取到坐标信息!请查看高德API配置是否正确')
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
AMapLoader.load({
|
||||||
|
key: config.aMapKey, // 申请好的Web端开发者Key,首次调用 load 时必填
|
||||||
|
version: "", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
|
||||||
|
plugins: [
|
||||||
|
"AMap.ToolBar",
|
||||||
|
"AMap.Autocomplete",
|
||||||
|
"AMap.PlaceSearch",
|
||||||
|
"AMap.Geolocation",
|
||||||
|
"AMap.Geocoder",
|
||||||
|
], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
|
||||||
|
AMapUI: {
|
||||||
|
// 是否加载 AMapUI,缺省不加载
|
||||||
|
version: "1.1", // AMapUI 缺省 1.1
|
||||||
|
plugins: ["misc/PositionPicker"], // 需要加载的 AMapUI ui插件
|
||||||
},
|
},
|
||||||
init() {
|
})
|
||||||
AMapLoader.load({
|
.then((AMap) => {
|
||||||
key: this.config.aMapKey, // 申请好的Web端开发者Key,首次调用 load 时必填
|
map = new AMap.Map("map-container", {
|
||||||
version: "", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
|
zoom: 12,
|
||||||
plugins: [
|
|
||||||
"AMap.ToolBar",
|
|
||||||
"AMap.Autocomplete",
|
|
||||||
"AMap.PlaceSearch",
|
|
||||||
"AMap.Geolocation",
|
|
||||||
"AMap.Geocoder",
|
|
||||||
], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
|
|
||||||
AMapUI: {
|
|
||||||
// 是否加载 AMapUI,缺省不加载
|
|
||||||
version: "1.1", // AMapUI 缺省 1.1
|
|
||||||
plugins: ["misc/PositionPicker"], // 需要加载的 AMapUI ui插件
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then((AMap) => {
|
|
||||||
let that = this;
|
|
||||||
this.map = new AMap.Map("map-container", {
|
|
||||||
zoom: 12,
|
|
||||||
});
|
|
||||||
that.map.addControl(new AMap.ToolBar());
|
|
||||||
that.map.addControl(new AMap.Autocomplete());
|
|
||||||
that.map.addControl(new AMap.PlaceSearch());
|
|
||||||
that.map.addControl(new AMap.Geocoder());
|
|
||||||
|
|
||||||
// 实例化Autocomplete
|
|
||||||
let autoOptions = {
|
|
||||||
city: "全国",
|
|
||||||
};
|
|
||||||
that.autoComplete = new AMap.Autocomplete(autoOptions); // 搜索
|
|
||||||
that.geocoder = new AMap.Geocoder(autoOptions);
|
|
||||||
|
|
||||||
that.positionPicker = new AMapUI.PositionPicker({
|
|
||||||
// 拖拽选点
|
|
||||||
mode: "dragMap",
|
|
||||||
map: that.map,
|
|
||||||
});
|
|
||||||
that.positionPicker.start();
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* 所有回显数据,都在positionResult里面
|
|
||||||
* 需要字段可以查找
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
that.positionPicker.on("success", function (positionResult) {
|
|
||||||
that.addrContent = positionResult;
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
console.warn('地图组件加载失败', e);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
searchOfMap(val) {
|
|
||||||
// 地图搜索
|
|
||||||
let that = this;
|
|
||||||
this.autoComplete.search(val, function (status, result) {
|
|
||||||
// 搜索成功时,result即是对应的匹配数据
|
|
||||||
if (status == "complete" && result.info == "OK") {
|
|
||||||
that.tips = result.tips;
|
|
||||||
} else {
|
|
||||||
that.tips = [];
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
map.addControl(new AMap.ToolBar());
|
||||||
selectAddr(location) {
|
map.addControl(new AMap.Autocomplete());
|
||||||
// 选择坐标
|
map.addControl(new AMap.PlaceSearch());
|
||||||
if (!location) {
|
map.addControl(new AMap.Geocoder());
|
||||||
Message.warning("请选择正确点位");
|
|
||||||
return false;
|
// 实例化Autocomplete
|
||||||
}
|
let autoOptions = {
|
||||||
const lnglat = [location.lng, location.lat];
|
city: "全国",
|
||||||
this.positionPicker.start(lnglat);
|
};
|
||||||
},
|
autoComplete = new AMap.Autocomplete(autoOptions); // 搜索
|
||||||
},
|
geocoder = new AMap.Geocoder(autoOptions);
|
||||||
mounted() {
|
|
||||||
this.init();
|
positionPicker = new AMapUI.PositionPicker({
|
||||||
},
|
// 拖拽选点
|
||||||
};
|
mode: "dragMap",
|
||||||
|
map: map,
|
||||||
|
});
|
||||||
|
positionPicker.start();
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 所有回显数据,都在positionResult里面
|
||||||
|
* 需要字段可以查找
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
positionPicker.on("success", function (positionResult) {
|
||||||
|
addrContent.value = positionResult;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.warn('地图组件加载失败', e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function searchOfMap(val) {
|
||||||
|
// 地图搜索
|
||||||
|
autoComplete.search(val, function (status, result) {
|
||||||
|
// 搜索成功时,result即是对应的匹配数据
|
||||||
|
if (status == "complete" && result.info == "OK") {
|
||||||
|
tips.value = result.tips;
|
||||||
|
} else {
|
||||||
|
tips.value = [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectAddr(location) {
|
||||||
|
// 选择坐标
|
||||||
|
if (!location) {
|
||||||
|
Message.warning("请选择正确点位");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const lnglat = [location.lng, location.lat];
|
||||||
|
positionPicker.start(lnglat);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
init();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
#map-container {
|
#map-container {
|
||||||
|
|||||||
@@ -26,93 +26,90 @@
|
|||||||
</div></el-dialog>
|
</div></el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { onMounted, reactive, ref } from "vue";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
import { aMapSwitch } from '@/config/index'
|
import { aMapSwitch } from '@/config/index'
|
||||||
import mapping from "@/components/map/index.vue";
|
import mapping from "@/components/map/index.vue";
|
||||||
import * as API_Setup from "@/api/common.js";
|
import * as API_Setup from "@/api/common.js";
|
||||||
export default {
|
|
||||||
components: { mapping },
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
aMapSwitch,
|
|
||||||
enableMap: false,
|
|
||||||
mapDefault: "select",
|
|
||||||
data: {
|
|
||||||
province: [], //省
|
|
||||||
city: [], //市
|
|
||||||
area: [], //区
|
|
||||||
street: [], //街道
|
|
||||||
},
|
|
||||||
chiosend: [],
|
|
||||||
|
|
||||||
};
|
const emit = defineEmits(['callback'])
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
|
|
||||||
this.chiosend = new Array(4).fill("");
|
const enableMap = ref(false)
|
||||||
|
const mapDefault = ref("select")
|
||||||
|
const data = reactive({
|
||||||
|
province: [], //省
|
||||||
|
city: [], //市
|
||||||
|
area: [], //区
|
||||||
|
street: [], //街道
|
||||||
|
})
|
||||||
|
const chiosend = ref([])
|
||||||
|
|
||||||
},
|
onMounted(() => {
|
||||||
methods: {
|
chiosend.value = new Array(4).fill("");
|
||||||
open() {
|
})
|
||||||
this.enableMap = true
|
|
||||||
this.init({ id: 0 }, 'province');
|
|
||||||
},
|
|
||||||
changeMap(val) {
|
|
||||||
this.mapDefault = val
|
|
||||||
|
|
||||||
|
function open() {
|
||||||
|
enableMap.value = true
|
||||||
|
init({ id: 0 }, 'province');
|
||||||
|
}
|
||||||
|
|
||||||
},
|
defineExpose({ open })
|
||||||
init(val, level = 'province', index) {
|
|
||||||
if (level == 0) {
|
function changeMap(val) {
|
||||||
// 说明选择到了街道,将街道id存入数组
|
mapDefault.value = val
|
||||||
this.chiosend.splice(3, 1, val);
|
}
|
||||||
|
|
||||||
|
function init(val, level = 'province', index) {
|
||||||
|
if (level == 0) {
|
||||||
|
// 说明选择到了街道,将街道id存入数组
|
||||||
|
chiosend.value.splice(3, 1, val);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
API_Setup.getRegion(val.id).then((res) => {
|
||||||
|
if (res.result.length && val.id !== 0) {
|
||||||
|
chiosend.value[index] = val
|
||||||
}
|
}
|
||||||
else {
|
else if (!res.result.length) {
|
||||||
API_Setup.getRegion(val.id).then((res) => {
|
chiosend.value[index] = val
|
||||||
if (res.result.length && val.id !== 0) {
|
|
||||||
this.chiosend[index] = val
|
|
||||||
}
|
|
||||||
else if(!res.result.length){
|
|
||||||
this.chiosend[index] = val
|
|
||||||
}
|
|
||||||
this.data[level] = res.result;
|
|
||||||
if (level == 'city') {
|
|
||||||
this.data.area = []
|
|
||||||
this.data.street = []
|
|
||||||
this.chiosend.splice(1, 3, "","","");
|
|
||||||
}
|
|
||||||
if (level == 'area') {
|
|
||||||
this.data.street = []
|
|
||||||
this.chiosend.splice(2, 2, "","");
|
|
||||||
}
|
|
||||||
if (level == 'street') {
|
|
||||||
this.chiosend.splice(3, 1, "");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
data[level] = res.result;
|
||||||
getAddress(center) {
|
if (level == 'city') {
|
||||||
this.$emit('callback', {
|
data.area = []
|
||||||
type: this.mapDefault,
|
data.street = []
|
||||||
data: center
|
chiosend.value.splice(1, 3, "", "", "");
|
||||||
})
|
|
||||||
this.enableMap = false;
|
|
||||||
},
|
|
||||||
// 选择完成
|
|
||||||
finished() {
|
|
||||||
if(!this.chiosend[0]){
|
|
||||||
Message.error("请选择地址")
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
const params = this.chiosend.filter((item) => item!=="" && item.value !== "");
|
if (level == 'area') {
|
||||||
this.enableMap = false;
|
data.street = []
|
||||||
this.$emit('callback', {
|
chiosend.value.splice(2, 2, "", "");
|
||||||
type: this.mapDefault,
|
}
|
||||||
data: params
|
if (level == 'street') {
|
||||||
})
|
chiosend.value.splice(3, 1, "");
|
||||||
},
|
}
|
||||||
},
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAddress(center) {
|
||||||
|
emit('callback', {
|
||||||
|
type: mapDefault.value,
|
||||||
|
data: center
|
||||||
|
})
|
||||||
|
enableMap.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择完成
|
||||||
|
function finished() {
|
||||||
|
if (!chiosend.value[0]) {
|
||||||
|
Message.error("请选择地址")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const params = chiosend.value.filter((item) => item !== "" && item.value !== "");
|
||||||
|
enableMap.value = false;
|
||||||
|
emit('callback', {
|
||||||
|
type: mapDefault.value,
|
||||||
|
data: params
|
||||||
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -9,143 +9,36 @@
|
|||||||
></el-cascader>
|
></el-cascader>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
import {getRegion} from '@/api/common.js';
|
import { ref, watch } from 'vue';
|
||||||
export default {
|
import { getRegion } from '@/api/common.js';
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
data: [], // 地区数据
|
|
||||||
addr: [] // 已选数据
|
|
||||||
};
|
|
||||||
},
|
|
||||||
props: ['addressId'],
|
|
||||||
mounted () {},
|
|
||||||
methods: {
|
|
||||||
change (val, selectedData) { // 选择地区
|
|
||||||
/**
|
|
||||||
* @returns [regionId,region]
|
|
||||||
*/
|
|
||||||
this.$emit('selected', [
|
|
||||||
val,
|
|
||||||
selectedData[selectedData.length - 1].__label.split('/')
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
loadData (item, callback) { // 加载数据
|
|
||||||
item.loading = true;
|
|
||||||
getRegion(item.value).then((res) => {
|
|
||||||
if (res.result.length <= 0) {
|
|
||||||
item.loading = false;
|
|
||||||
} else {
|
|
||||||
res.result.forEach((child) => {
|
|
||||||
item.loading = false;
|
|
||||||
|
|
||||||
let data = {
|
const props = defineProps(['addressId']);
|
||||||
value: child.id,
|
const emit = defineEmits(['selected']);
|
||||||
label: child.name,
|
|
||||||
loading: false,
|
|
||||||
children: []
|
|
||||||
};
|
|
||||||
|
|
||||||
if (child.level === 'street' || item.label === '香港特别行政区') {
|
const data = ref([]); // 地区数据
|
||||||
item.children.push({
|
const addr = ref([]); // 已选数据
|
||||||
value: child.id,
|
|
||||||
label: child.name
|
function change (val, selectedData) { // 选择地区
|
||||||
});
|
/**
|
||||||
} else {
|
* @returns [regionId,region]
|
||||||
item.children.push(data);
|
*/
|
||||||
}
|
emit('selected', [
|
||||||
});
|
val,
|
||||||
callback();
|
selectedData[selectedData.length - 1].__label.split('/')
|
||||||
}
|
]);
|
||||||
});
|
}
|
||||||
},
|
|
||||||
async init () { // 初始化地图数据
|
function loadData (item, callback) { // 加载数据
|
||||||
let data = await getRegion(0);
|
item.loading = true;
|
||||||
let arr = [];
|
getRegion(item.value).then((res) => {
|
||||||
data.result.forEach((item) => {
|
if (res.result.length <= 0) {
|
||||||
let obj;
|
item.loading = false;
|
||||||
// 台湾省做处理
|
} else {
|
||||||
if (item.name === '台湾省') {
|
res.result.forEach((child) => {
|
||||||
obj = {
|
item.loading = false;
|
||||||
value: item.id,
|
|
||||||
label: item.name
|
let data = {
|
||||||
};
|
|
||||||
} else {
|
|
||||||
obj = {
|
|
||||||
value: item.id,
|
|
||||||
label: item.name,
|
|
||||||
loading: false,
|
|
||||||
children: []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
arr.push(obj);
|
|
||||||
});
|
|
||||||
this.data = arr;
|
|
||||||
console.warn('init');
|
|
||||||
},
|
|
||||||
async reviewData () {
|
|
||||||
// 数据回显
|
|
||||||
let addr = JSON.parse(JSON.stringify(this.addressId.split(',')));
|
|
||||||
let length = addr.length;
|
|
||||||
let data = await getRegion(0);
|
|
||||||
let arr0 = [];
|
|
||||||
let arr1 = [];
|
|
||||||
let arr2 = [];
|
|
||||||
// 第一级数据
|
|
||||||
data.result.forEach((item) => {
|
|
||||||
let obj;
|
|
||||||
// 台湾省做处理
|
|
||||||
if (item.name === '台湾省') {
|
|
||||||
obj = {
|
|
||||||
value: item.id,
|
|
||||||
label: item.name
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
obj = {
|
|
||||||
value: item.id,
|
|
||||||
label: item.name,
|
|
||||||
loading: false,
|
|
||||||
children: []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
arr0.push(obj);
|
|
||||||
});
|
|
||||||
// 根据选择的数据来加载数据列表
|
|
||||||
if (length > 0) {
|
|
||||||
let children = await getRegion(addr[0]);
|
|
||||||
children = this.handleData(children.result);
|
|
||||||
arr0.forEach((e) => {
|
|
||||||
if (e.value === addr[0]) {
|
|
||||||
e.children = arr1 = children;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (length > 1) {
|
|
||||||
let children = await getRegion(addr[1]);
|
|
||||||
children = this.handleData(children.result);
|
|
||||||
arr1.forEach((e) => {
|
|
||||||
if (e.value === addr[1]) {
|
|
||||||
e.children = arr2 = children;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (length > 2) {
|
|
||||||
let children = await getRegion(addr[2]);
|
|
||||||
children = this.handleData(children.result);
|
|
||||||
arr2.forEach((e) => {
|
|
||||||
if (e.value === addr[2]) {
|
|
||||||
e.children = children;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.data = arr0;
|
|
||||||
this.addr = addr;
|
|
||||||
},
|
|
||||||
handleData (data) {
|
|
||||||
// 处理接口数据
|
|
||||||
let item = [];
|
|
||||||
data.forEach((child) => {
|
|
||||||
let obj = {
|
|
||||||
value: child.id,
|
value: child.id,
|
||||||
label: child.name,
|
label: child.name,
|
||||||
loading: false,
|
loading: false,
|
||||||
@@ -153,30 +46,133 @@ export default {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (child.level === 'street' || item.label === '香港特别行政区') {
|
if (child.level === 'street' || item.label === '香港特别行政区') {
|
||||||
item.push({
|
item.children.push({
|
||||||
value: child.id,
|
value: child.id,
|
||||||
label: child.name
|
label: child.name
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
item.push(obj);
|
item.children.push(data);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return item;
|
callback();
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
watch: {
|
}
|
||||||
addressId: {
|
|
||||||
handler: function (v) {
|
async function init () { // 初始化地图数据
|
||||||
if (v) {
|
let res = await getRegion(0);
|
||||||
this.reviewData();
|
let arr = [];
|
||||||
} else {
|
res.result.forEach((item) => {
|
||||||
this.init();
|
let obj;
|
||||||
}
|
// 台湾省做处理
|
||||||
},
|
if (item.name === '台湾省') {
|
||||||
immediate: true
|
obj = {
|
||||||
|
value: item.id,
|
||||||
|
label: item.name
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
obj = {
|
||||||
|
value: item.id,
|
||||||
|
label: item.name,
|
||||||
|
loading: false,
|
||||||
|
children: []
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
arr.push(obj);
|
||||||
|
});
|
||||||
|
data.value = arr;
|
||||||
|
console.warn('init');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function reviewData () {
|
||||||
|
// 数据回显
|
||||||
|
let addrArr = JSON.parse(JSON.stringify(props.addressId.split(',')));
|
||||||
|
let length = addrArr.length;
|
||||||
|
let res = await getRegion(0);
|
||||||
|
let arr0 = [];
|
||||||
|
let arr1 = [];
|
||||||
|
let arr2 = [];
|
||||||
|
// 第一级数据
|
||||||
|
res.result.forEach((item) => {
|
||||||
|
let obj;
|
||||||
|
// 台湾省做处理
|
||||||
|
if (item.name === '台湾省') {
|
||||||
|
obj = {
|
||||||
|
value: item.id,
|
||||||
|
label: item.name
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
obj = {
|
||||||
|
value: item.id,
|
||||||
|
label: item.name,
|
||||||
|
loading: false,
|
||||||
|
children: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
arr0.push(obj);
|
||||||
|
});
|
||||||
|
// 根据选择的数据来加载数据列表
|
||||||
|
if (length > 0) {
|
||||||
|
let children = await getRegion(addrArr[0]);
|
||||||
|
children = handleData(children.result);
|
||||||
|
arr0.forEach((e) => {
|
||||||
|
if (e.value === addrArr[0]) {
|
||||||
|
e.children = arr1 = children;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
if (length > 1) {
|
||||||
|
let children = await getRegion(addrArr[1]);
|
||||||
|
children = handleData(children.result);
|
||||||
|
arr1.forEach((e) => {
|
||||||
|
if (e.value === addrArr[1]) {
|
||||||
|
e.children = arr2 = children;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (length > 2) {
|
||||||
|
let children = await getRegion(addrArr[2]);
|
||||||
|
children = handleData(children.result);
|
||||||
|
arr2.forEach((e) => {
|
||||||
|
if (e.value === addrArr[2]) {
|
||||||
|
e.children = children;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
data.value = arr0;
|
||||||
|
addr.value = addrArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleData (data) {
|
||||||
|
// 处理接口数据
|
||||||
|
let item = [];
|
||||||
|
data.forEach((child) => {
|
||||||
|
let obj = {
|
||||||
|
value: child.id,
|
||||||
|
label: child.name,
|
||||||
|
loading: false,
|
||||||
|
children: []
|
||||||
|
};
|
||||||
|
|
||||||
|
if (child.level === 'street' || item.label === '香港特别行政区') {
|
||||||
|
item.push({
|
||||||
|
value: child.id,
|
||||||
|
label: child.name
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
item.push(obj);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => props.addressId, (v) => {
|
||||||
|
if (v) {
|
||||||
|
reviewData();
|
||||||
|
} else {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
}, { immediate: true });
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,77 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div></div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { Modal } from "@/utils/message";
|
|
||||||
import { getIMDetail } from "@/api/common";
|
|
||||||
import Storage from "@/plugins/storage";
|
|
||||||
import { getMemberMsg } from "@/api/login";
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
Storage,
|
|
||||||
IMLink: "",
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
promptLogin() {
|
|
||||||
Modal.confirm({
|
|
||||||
title: "温馨提示",
|
|
||||||
content: "请登录后执行此操作",
|
|
||||||
okText: "立即登录",
|
|
||||||
cancelText: "取消",
|
|
||||||
onOk: () => {
|
|
||||||
this.$router.push({
|
|
||||||
path: "/login",
|
|
||||||
query: {
|
|
||||||
rePath: this.$route.path,
|
|
||||||
query: JSON.stringify(this.$route.query || {}),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 跳转im客服
|
|
||||||
async IMService(id, goodsId, skuId) {
|
|
||||||
if (!Storage.getItem("accessToken")) {
|
|
||||||
this.promptLogin();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await this.getIMDetailMethods();
|
|
||||||
const userInfo = await getMemberMsg();
|
|
||||||
if (!userInfo.success) {
|
|
||||||
this.promptLogin();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const accessToken = Storage.getItem("accessToken");
|
|
||||||
const storeId = id || this.storeMsg?.storeId;
|
|
||||||
const baseUrl = `${this.IMLink}?token=${accessToken}&id=${storeId}`;
|
|
||||||
|
|
||||||
if (goodsId && skuId) {
|
|
||||||
window.open(`${baseUrl}&goodsId=${goodsId}&skuId=${skuId}`);
|
|
||||||
} else {
|
|
||||||
window.open(baseUrl);
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// token 过期等场景由 request 拦截器处理;未登录时避免未捕获异常
|
|
||||||
if (!Storage.getItem("accessToken")) {
|
|
||||||
this.promptLogin();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 获取im信息
|
|
||||||
async getIMDetailMethods() {
|
|
||||||
let res = await getIMDetail();
|
|
||||||
if (res.success) {
|
|
||||||
this.IMLink = res.result;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
|
||||||
@@ -90,138 +90,143 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { computed, onMounted, ref } from "vue";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
import { getCategory } from "@/api/goods";
|
import { getCategory } from "@/api/goods";
|
||||||
import storage from "@/plugins/storage.js";
|
import storage from "@/plugins/storage.js";
|
||||||
import { ArrowRight } from "@element-plus/icons-vue";
|
import { ArrowRight } from "@element-plus/icons-vue";
|
||||||
export default {
|
import { useAppStore } from "@/stores/app";
|
||||||
components: { ArrowRight },
|
|
||||||
name: "GoodsListNav",
|
defineOptions({ name: "GoodsListNav" });
|
||||||
props: {
|
|
||||||
showAlways: {
|
const props = defineProps({
|
||||||
// 总是显示下拉分类
|
showAlways: {
|
||||||
default: false,
|
// 总是显示下拉分类
|
||||||
type: Boolean,
|
default: false,
|
||||||
},
|
type: Boolean,
|
||||||
showNavBar: {
|
},
|
||||||
// 显示全部商品分类右侧导航条
|
showNavBar: {
|
||||||
default: true,
|
// 显示全部商品分类右侧导航条
|
||||||
type: Boolean,
|
default: true,
|
||||||
},
|
type: Boolean,
|
||||||
hover: {
|
},
|
||||||
default: false,
|
hover: {
|
||||||
type: Boolean,
|
default: false,
|
||||||
},
|
type: Boolean,
|
||||||
large: {
|
},
|
||||||
// 是否更高的高度
|
large: {
|
||||||
default: false,
|
// 是否更高的高度
|
||||||
type: Boolean,
|
default: false,
|
||||||
},
|
type: Boolean,
|
||||||
opacity: {
|
},
|
||||||
// 是否背景透明
|
opacity: {
|
||||||
default: false,
|
// 是否背景透明
|
||||||
type: Boolean,
|
default: false,
|
||||||
},
|
type: Boolean,
|
||||||
useClass:{
|
},
|
||||||
type:null,
|
useClass: {
|
||||||
default:''
|
type: null,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const appStore = useAppStore();
|
||||||
|
|
||||||
|
const panel = ref(false); // 二级分类展示
|
||||||
|
const panelIndex = ref(-1);
|
||||||
|
const panelData = ref([]); // 二级分类数据
|
||||||
|
const showFirstList = ref(false); // 始终展示一级列表
|
||||||
|
const cateList = ref([]); // 商品分类
|
||||||
|
|
||||||
|
// 导航列表
|
||||||
|
const navList = computed(() => {
|
||||||
|
if (storage.getItem("navList")) {
|
||||||
|
return JSON.parse(storage.getItem("navList"));
|
||||||
|
}
|
||||||
|
return { list: [] };
|
||||||
|
});
|
||||||
|
|
||||||
|
function showFirstLists() {
|
||||||
|
showFirstList.value = true;
|
||||||
|
if (
|
||||||
|
localStorage.getItem("category") &&
|
||||||
|
localStorage.getItem("category_expiration_time")
|
||||||
|
) {
|
||||||
|
// this.getCate();
|
||||||
|
// 如果缓存过期,则获取最新的信息
|
||||||
|
if (new Date() > localStorage.getItem("category_expiration_time")) {
|
||||||
|
getCate();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
},
|
cateList.value = JSON.parse(localStorage.getItem("category"));
|
||||||
data() {
|
// Message.info(cateList)
|
||||||
return {
|
}
|
||||||
panel: false, // 二级分类展示
|
}
|
||||||
panelIndex: -1,
|
|
||||||
panelData: [], // 二级分类数据
|
function getCate() {
|
||||||
showFirstList: false, // 始终展示一级列表
|
// 获取分类数据
|
||||||
cateList: [], // 商品分类
|
if (props.hover) return false;
|
||||||
};
|
getCategory(0).then((res) => {
|
||||||
},
|
if (res.success) {
|
||||||
computed: {
|
cateList.value = res.result;
|
||||||
navList() {
|
appStore.setCategory(res.result);
|
||||||
// 导航列表
|
// 过期时间
|
||||||
if (storage.getItem("navList")) {
|
var expirationTime = new Date().setHours(new Date().getHours() + 1);
|
||||||
return JSON.parse(storage.getItem("navList"));
|
// 存放过期时间
|
||||||
}
|
localStorage.setItem("category_expiration_time", expirationTime);
|
||||||
return { list: [] };
|
// 存放分类信息
|
||||||
},
|
localStorage.setItem("category", JSON.stringify(res.result));
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
showFirstLists(){
|
|
||||||
this.showFirstList = true;
|
|
||||||
if(
|
|
||||||
localStorage.getItem("category") &&
|
|
||||||
localStorage.getItem("category_expiration_time")
|
|
||||||
) {
|
|
||||||
// this.getCate();
|
|
||||||
// 如果缓存过期,则获取最新的信息
|
|
||||||
if (new Date() > localStorage.getItem("category_expiration_time")) {
|
|
||||||
this.getCate();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.cateList = JSON.parse(localStorage.getItem("category"));
|
|
||||||
// Message.info(cateList)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getCate() {
|
|
||||||
// 获取分类数据
|
|
||||||
if (this.hover) return false;
|
|
||||||
getCategory(0).then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
this.cateList = res.result;
|
|
||||||
this.$store.commit("SET_CATEGORY", res.result);
|
|
||||||
// 过期时间
|
|
||||||
var expirationTime = new Date().setHours(new Date().getHours() + 1);
|
|
||||||
// 存放过期时间
|
|
||||||
localStorage.setItem("category_expiration_time", expirationTime);
|
|
||||||
// 存放分类信息
|
|
||||||
localStorage.setItem("category", JSON.stringify(res.result));
|
|
||||||
}
|
|
||||||
}).catch((err) => {
|
|
||||||
console.warn('获取分类失败', err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
showDetail(index) {
|
|
||||||
// 展示全部分类
|
|
||||||
this.panel = true;
|
|
||||||
this.panelIndex = index;
|
|
||||||
this.panelData = this.cateList[index].children;
|
|
||||||
},
|
|
||||||
closePanel() {
|
|
||||||
this.showFirstList = false;
|
|
||||||
this.panel = false;
|
|
||||||
this.panelIndex = -1;
|
|
||||||
},
|
|
||||||
goGoodsList(id, secondId, firstId) {
|
|
||||||
// 分类共有三级,传全部分类过去
|
|
||||||
const arr = [firstId, secondId, id];
|
|
||||||
if (!arr[1]) {
|
|
||||||
arr.splice(0, 2);
|
|
||||||
}
|
|
||||||
if (!arr[0]) {
|
|
||||||
arr.shift();
|
|
||||||
}
|
|
||||||
this.$router.push({
|
|
||||||
path: "/goodsList",
|
|
||||||
query: { categoryId: arr.toString() },
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
if (
|
|
||||||
localStorage.getItem("category") &&
|
|
||||||
localStorage.getItem("category_expiration_time")
|
|
||||||
) {
|
|
||||||
// 如果缓存过期,则获取最新的信息
|
|
||||||
if (new Date() > localStorage.getItem("category_expiration_time")) {
|
|
||||||
this.getCate();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.cateList = JSON.parse(localStorage.getItem("category"));
|
|
||||||
} else {
|
|
||||||
this.getCate();
|
|
||||||
}
|
}
|
||||||
},
|
}).catch((err) => {
|
||||||
};
|
console.warn('获取分类失败', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function showDetail(index) {
|
||||||
|
// 展示全部分类
|
||||||
|
panel.value = true;
|
||||||
|
panelIndex.value = index;
|
||||||
|
panelData.value = cateList.value[index].children;
|
||||||
|
}
|
||||||
|
|
||||||
|
function closePanel() {
|
||||||
|
showFirstList.value = false;
|
||||||
|
panel.value = false;
|
||||||
|
panelIndex.value = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function goGoodsList(id, secondId, firstId) {
|
||||||
|
// 分类共有三级,传全部分类过去
|
||||||
|
const arr = [firstId, secondId, id];
|
||||||
|
if (!arr[1]) {
|
||||||
|
arr.splice(0, 2);
|
||||||
|
}
|
||||||
|
if (!arr[0]) {
|
||||||
|
arr.shift();
|
||||||
|
}
|
||||||
|
router.push({
|
||||||
|
path: "/goodsList",
|
||||||
|
query: { categoryId: arr.toString() },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (
|
||||||
|
localStorage.getItem("category") &&
|
||||||
|
localStorage.getItem("category_expiration_time")
|
||||||
|
) {
|
||||||
|
// 如果缓存过期,则获取最新的信息
|
||||||
|
if (new Date() > localStorage.getItem("category_expiration_time")) {
|
||||||
|
getCate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cateList.value = JSON.parse(localStorage.getItem("category"));
|
||||||
|
} else {
|
||||||
|
getCate();
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="item-class-show">
|
<div class="item-class-show" ref="rootEl">
|
||||||
<div class="head-bar">
|
<div class="head-bar">
|
||||||
<!-- 有商品分类展示商品分类 -->
|
<!-- 有商品分类展示商品分类 -->
|
||||||
<template v-if="$route.query.categoryId">
|
<template v-if="$route.query.categoryId">
|
||||||
@@ -116,246 +116,253 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { ArrowDown, ArrowRight, ArrowUp, Close } from '@element-plus/icons-vue';
|
import { ArrowDown, ArrowRight, ArrowUp, Close } from '@element-plus/icons-vue';
|
||||||
import * as APIGoods from '@/api/goods';
|
import * as APIGoods from '@/api/goods';
|
||||||
export default {
|
import { useAppStore } from '@/stores/app';
|
||||||
components: { ArrowDown, ArrowRight, ArrowUp, Close },
|
|
||||||
name: 'GoodsClassNav',
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
tabBar: {
|
|
||||||
name: '',
|
|
||||||
children: [],
|
|
||||||
first: null,
|
|
||||||
second: null
|
|
||||||
},
|
|
||||||
showTagCount: 5, // 展示的搜索项数量
|
|
||||||
tagsContent: [
|
|
||||||
// 标签
|
|
||||||
{
|
|
||||||
key: '品牌',
|
|
||||||
more: false,
|
|
||||||
show: true,
|
|
||||||
values: []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
selectedItem: [], // 已选分类集合 顶部展示
|
|
||||||
brandIds: [], // 品牌id合集
|
|
||||||
params: {}, // 请求参数
|
|
||||||
rowOverflow: {} // 一行排不下时才显示更多
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
cateList () { // 商品分类
|
|
||||||
return this.$store.state.category || []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
selectedItem: {
|
|
||||||
// 监听已选条件,来调用列表接口
|
|
||||||
handler (val) {
|
|
||||||
let classification = [];
|
|
||||||
this.params.brandId = ''
|
|
||||||
this.params.prop = ''
|
|
||||||
val.forEach((item) => {
|
|
||||||
if (item.type === '品牌') {
|
|
||||||
this.params.brandId = this.brandIds.join('@');
|
|
||||||
} else {
|
|
||||||
const nameArr = item.name.split('、');
|
|
||||||
nameArr.forEach((name) => {
|
|
||||||
classification.push(item.type + '_' + name);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.params.prop = classification.join('@');
|
|
||||||
this.getFilterList(this.params);
|
|
||||||
this.$emit('getParams', this.params);
|
|
||||||
},
|
|
||||||
deep: true
|
|
||||||
},
|
|
||||||
'$route': { // 监听路由
|
|
||||||
handler (val, oVal) {
|
|
||||||
if (this.$route.query.categoryId) {
|
|
||||||
let cateId = this.$route.query.categoryId.split(',')
|
|
||||||
Object.assign(this.params, this.$route.query)
|
|
||||||
this.params.categoryId = cateId[cateId.length - 1]
|
|
||||||
} else {
|
|
||||||
Object.assign(this.params, this.$route.query)
|
|
||||||
}
|
|
||||||
this.getFilterList(this.params)
|
|
||||||
this.getNav()
|
|
||||||
},
|
|
||||||
deep: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getNav () { // 获取商品分类,分类下展示
|
|
||||||
if (!this.$route.query.categoryId) return
|
|
||||||
if (!this.cateList.length) { // 商品分类存储在localstorage,接口未调用成功前再次刷新数据
|
|
||||||
setTimeout(() => {
|
|
||||||
this.getNav()
|
|
||||||
}, 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const arr = this.$route.query.categoryId.split(',')
|
|
||||||
const source = this.cateList.find((e) => e.id === arr[0])
|
|
||||||
if (!source) return
|
|
||||||
const tabBar = {
|
|
||||||
id: source.id,
|
|
||||||
name: source.name,
|
|
||||||
parentId: source.parentId,
|
|
||||||
children: source.children || [],
|
|
||||||
first: null,
|
|
||||||
second: null,
|
|
||||||
}
|
|
||||||
if (arr.length > 1) {
|
|
||||||
tabBar.first = tabBar.children.find((e) => e.id === arr[1]) || null
|
|
||||||
}
|
|
||||||
if (arr.length > 2 && tabBar.first && tabBar.first.children) {
|
|
||||||
tabBar.second = tabBar.first.children.find((e) => e.id === arr[2]) || null
|
|
||||||
}
|
|
||||||
this.tabBar = tabBar
|
|
||||||
},
|
|
||||||
cateClick (item, index) { // 点选分类
|
|
||||||
switch (index) {
|
|
||||||
case 1:
|
|
||||||
this.$router.push({
|
|
||||||
path: '/goodsList',
|
|
||||||
query: {categoryId: item.id}
|
|
||||||
})
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
this.$router.push({
|
|
||||||
path: '/goodsList',
|
|
||||||
query: {categoryId: [item.parentId, item.id].toString()}
|
|
||||||
})
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
this.$router.push({
|
|
||||||
path: '/goodsList',
|
|
||||||
query: {categoryId: [this.tabBar.id, item.parentId, item.id].toString()}
|
|
||||||
})
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
isGroupSelected (index) {
|
|
||||||
const group = this.tagsContent[index];
|
|
||||||
if (!group) return false;
|
|
||||||
return this.selectedItem.some((selected) => selected.type === group.key);
|
|
||||||
},
|
|
||||||
selectAll (index) {
|
|
||||||
const group = this.tagsContent[index];
|
|
||||||
if (!group) return;
|
|
||||||
const selectedIndex = this.selectedItem.findIndex((selected) => selected.type === group.key);
|
|
||||||
if (selectedIndex === -1) return;
|
|
||||||
this.cancelSelected(this.selectedItem[selectedIndex], selectedIndex);
|
|
||||||
},
|
|
||||||
selectBrand (item, index) {
|
|
||||||
const type = this.tagsContent[index].key;
|
|
||||||
if (this.selectedItem.some((selected) => selected.type === type)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.selectedItem.push({
|
|
||||||
type,
|
|
||||||
name: item
|
|
||||||
});
|
|
||||||
this.tagsContent[index].show = false;
|
|
||||||
if (index === 0) {
|
|
||||||
this.appendBrandIdsByNames([item]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
cancelSelected (item, index) {
|
defineOptions({ name: 'GoodsClassNav' });
|
||||||
// 顶部栏 取消已选中的项
|
|
||||||
this.selectedItem.splice(index, 1);
|
|
||||||
|
|
||||||
this.tagsContent.forEach((tag, index) => {
|
const emit = defineEmits(['getParams']);
|
||||||
if (tag.key === item.type) {
|
|
||||||
tag.show = true;
|
const route = useRoute();
|
||||||
tag.more = false;
|
const router = useRouter();
|
||||||
}
|
const appStore = useAppStore();
|
||||||
});
|
|
||||||
if (item.type === '品牌') {
|
const tabBar = ref({
|
||||||
this.brandIds = [];
|
name: '',
|
||||||
}
|
children: [],
|
||||||
},
|
first: null,
|
||||||
moreBrand (index) {
|
second: null
|
||||||
// 更多按钮
|
});
|
||||||
const flag = !this.tagsContent[index].more
|
const showTagCount = ref(5); // 展示的搜索项数量
|
||||||
this.tagsContent[index]['more'] = flag
|
const tagsContent = ref([
|
||||||
this.$nextTick(() => this.measureRows())
|
// 标签
|
||||||
},
|
{
|
||||||
measureRows () {
|
key: '品牌',
|
||||||
if (!this.$el) return
|
more: false,
|
||||||
const next = {}
|
show: true,
|
||||||
const brand = this.$el.querySelector('.brand ul')
|
values: []
|
||||||
if (brand && brand.offsetParent) {
|
}
|
||||||
next[0] = !!(this.tagsContent[0] && this.tagsContent[0].more) || brand.scrollHeight > brand.clientHeight + 1
|
]);
|
||||||
}
|
const selectedItem = ref([]); // 已选分类集合 顶部展示
|
||||||
this.$el.querySelectorAll('.other .list').forEach((list) => {
|
const brandIds = ref([]); // 品牌id合集
|
||||||
if (!list.offsetParent) return
|
const params = reactive({}); // 请求参数
|
||||||
const index = Number(list.dataset.index)
|
const rowOverflow = ref({}); // 一行排不下时才显示更多
|
||||||
const tag = this.tagsContent[index]
|
const rootEl = ref(null); // 根元素,用于测量行溢出
|
||||||
next[index] = !!(tag && tag.more) || list.scrollHeight > list.clientHeight + 1
|
|
||||||
})
|
const cateList = computed(() => appStore.category || []); // 商品分类
|
||||||
const prev = this.rowOverflow
|
|
||||||
const keys = new Set([...Object.keys(prev), ...Object.keys(next)])
|
// 监听已选条件,来调用列表接口
|
||||||
const changed = [...keys].some((key) => !!prev[key] !== !!next[key])
|
watch(selectedItem, (val) => {
|
||||||
if (changed) this.rowOverflow = next
|
let classification = [];
|
||||||
},
|
params.brandId = ''
|
||||||
appendBrandIdsByNames (names) {
|
params.prop = ''
|
||||||
const selectedNames = names || [];
|
val.forEach((item) => {
|
||||||
const brands = this.tagsContent[0] && this.tagsContent[0].values ? this.tagsContent[0].values : [];
|
if (item.type === '品牌') {
|
||||||
brands.forEach((val) => {
|
params.brandId = brandIds.value.join('@');
|
||||||
if (selectedNames.includes(val.name) && val.value && !this.brandIds.includes(val.value)) {
|
|
||||||
this.brandIds.push(val.value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getFilterList (params) {
|
|
||||||
// 筛选、分类 列表
|
|
||||||
APIGoods.filterList(params).then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
const data = res.result;
|
|
||||||
const hiddenKeys = new Set(this.selectedItem.map((item) => item.type));
|
|
||||||
this.tagsContent = [{
|
|
||||||
key: '品牌',
|
|
||||||
more: false,
|
|
||||||
show: !hiddenKeys.has('品牌'),
|
|
||||||
values: data.brands || []
|
|
||||||
}].concat(data.paramOptions || []);
|
|
||||||
this.tagsContent.forEach((item) => {
|
|
||||||
item.show = !hiddenKeys.has(item.key);
|
|
||||||
item.more = false;
|
|
||||||
});
|
|
||||||
this.$nextTick(() => this.measureRows())
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 展示更多搜索项
|
|
||||||
moreOptions () {
|
|
||||||
this.showTagCount = this.showTagCount === 5 ? 100 : 5
|
|
||||||
this.$nextTick(() => this.measureRows())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
// 有分类id就根据id搜索
|
|
||||||
if (this.$route.query.categoryId) {
|
|
||||||
let cateId = this.$route.query.categoryId.split(',')
|
|
||||||
Object.assign(this.params, this.$route.query)
|
|
||||||
this.params.categoryId = cateId[cateId.length - 1]
|
|
||||||
} else {
|
} else {
|
||||||
Object.assign(this.params, this.$route.query)
|
const nameArr = item.name.split('、');
|
||||||
|
nameArr.forEach((name) => {
|
||||||
|
classification.push(item.type + '_' + name);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
this.getFilterList(this.params);
|
});
|
||||||
this.getNav();
|
params.prop = classification.join('@');
|
||||||
window.addEventListener('resize', this.measureRows)
|
getFilterList(params);
|
||||||
},
|
emit('getParams', params);
|
||||||
beforeUnmount () {
|
}, { deep: true });
|
||||||
window.removeEventListener('resize', this.measureRows)
|
|
||||||
|
// 监听路由
|
||||||
|
watch(() => route.query, () => {
|
||||||
|
if (route.query.categoryId) {
|
||||||
|
let cateId = route.query.categoryId.split(',')
|
||||||
|
Object.assign(params, route.query)
|
||||||
|
params.categoryId = cateId[cateId.length - 1]
|
||||||
|
} else {
|
||||||
|
Object.assign(params, route.query)
|
||||||
|
}
|
||||||
|
getFilterList(params)
|
||||||
|
getNav()
|
||||||
|
}, { deep: true });
|
||||||
|
|
||||||
|
function getNav () { // 获取商品分类,分类下展示
|
||||||
|
if (!route.query.categoryId) return
|
||||||
|
if (!cateList.value.length) { // 商品分类存储在localstorage,接口未调用成功前再次刷新数据
|
||||||
|
setTimeout(() => {
|
||||||
|
getNav()
|
||||||
|
}, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const arr = route.query.categoryId.split(',')
|
||||||
|
const source = cateList.value.find((e) => e.id === arr[0])
|
||||||
|
if (!source) return
|
||||||
|
const newTabBar = {
|
||||||
|
id: source.id,
|
||||||
|
name: source.name,
|
||||||
|
parentId: source.parentId,
|
||||||
|
children: source.children || [],
|
||||||
|
first: null,
|
||||||
|
second: null,
|
||||||
|
}
|
||||||
|
if (arr.length > 1) {
|
||||||
|
newTabBar.first = newTabBar.children.find((e) => e.id === arr[1]) || null
|
||||||
|
}
|
||||||
|
if (arr.length > 2 && newTabBar.first && newTabBar.first.children) {
|
||||||
|
newTabBar.second = newTabBar.first.children.find((e) => e.id === arr[2]) || null
|
||||||
|
}
|
||||||
|
tabBar.value = newTabBar
|
||||||
|
}
|
||||||
|
|
||||||
|
function cateClick (item, index) { // 点选分类
|
||||||
|
switch (index) {
|
||||||
|
case 1:
|
||||||
|
router.push({
|
||||||
|
path: '/goodsList',
|
||||||
|
query: {categoryId: item.id}
|
||||||
|
})
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
router.push({
|
||||||
|
path: '/goodsList',
|
||||||
|
query: {categoryId: [item.parentId, item.id].toString()}
|
||||||
|
})
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
router.push({
|
||||||
|
path: '/goodsList',
|
||||||
|
query: {categoryId: [tabBar.value.id, item.parentId, item.id].toString()}
|
||||||
|
})
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isGroupSelected (index) {
|
||||||
|
const group = tagsContent.value[index];
|
||||||
|
if (!group) return false;
|
||||||
|
return selectedItem.value.some((selected) => selected.type === group.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectAll (index) {
|
||||||
|
const group = tagsContent.value[index];
|
||||||
|
if (!group) return;
|
||||||
|
const selectedIndex = selectedItem.value.findIndex((selected) => selected.type === group.key);
|
||||||
|
if (selectedIndex === -1) return;
|
||||||
|
cancelSelected(selectedItem.value[selectedIndex], selectedIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectBrand (item, index) {
|
||||||
|
const type = tagsContent.value[index].key;
|
||||||
|
if (selectedItem.value.some((selected) => selected.type === type)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectedItem.value.push({
|
||||||
|
type,
|
||||||
|
name: item
|
||||||
|
});
|
||||||
|
tagsContent.value[index].show = false;
|
||||||
|
if (index === 0) {
|
||||||
|
appendBrandIdsByNames([item]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelSelected (item, index) {
|
||||||
|
// 顶部栏 取消已选中的项
|
||||||
|
selectedItem.value.splice(index, 1);
|
||||||
|
|
||||||
|
tagsContent.value.forEach((tag, index) => {
|
||||||
|
if (tag.key === item.type) {
|
||||||
|
tag.show = true;
|
||||||
|
tag.more = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (item.type === '品牌') {
|
||||||
|
brandIds.value = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function moreBrand (index) {
|
||||||
|
// 更多按钮
|
||||||
|
const flag = !tagsContent.value[index].more
|
||||||
|
tagsContent.value[index]['more'] = flag
|
||||||
|
nextTick(() => measureRows())
|
||||||
|
}
|
||||||
|
|
||||||
|
function measureRows () {
|
||||||
|
if (!rootEl.value) return
|
||||||
|
const next = {}
|
||||||
|
const brand = rootEl.value.querySelector('.brand ul')
|
||||||
|
if (brand && brand.offsetParent) {
|
||||||
|
next[0] = !!(tagsContent.value[0] && tagsContent.value[0].more) || brand.scrollHeight > brand.clientHeight + 1
|
||||||
|
}
|
||||||
|
rootEl.value.querySelectorAll('.other .list').forEach((list) => {
|
||||||
|
if (!list.offsetParent) return
|
||||||
|
const index = Number(list.dataset.index)
|
||||||
|
const tag = tagsContent.value[index]
|
||||||
|
next[index] = !!(tag && tag.more) || list.scrollHeight > list.clientHeight + 1
|
||||||
|
})
|
||||||
|
const prev = rowOverflow.value
|
||||||
|
const keys = new Set([...Object.keys(prev), ...Object.keys(next)])
|
||||||
|
const changed = [...keys].some((key) => !!prev[key] !== !!next[key])
|
||||||
|
if (changed) rowOverflow.value = next
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendBrandIdsByNames (names) {
|
||||||
|
const selectedNames = names || [];
|
||||||
|
const brands = tagsContent.value[0] && tagsContent.value[0].values ? tagsContent.value[0].values : [];
|
||||||
|
brands.forEach((val) => {
|
||||||
|
if (selectedNames.includes(val.name) && val.value && !brandIds.value.includes(val.value)) {
|
||||||
|
brandIds.value.push(val.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFilterList (params) {
|
||||||
|
// 筛选、分类 列表
|
||||||
|
APIGoods.filterList(params).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
const data = res.result;
|
||||||
|
const hiddenKeys = new Set(selectedItem.value.map((item) => item.type));
|
||||||
|
tagsContent.value = [{
|
||||||
|
key: '品牌',
|
||||||
|
more: false,
|
||||||
|
show: !hiddenKeys.has('品牌'),
|
||||||
|
values: data.brands || []
|
||||||
|
}].concat(data.paramOptions || []);
|
||||||
|
tagsContent.value.forEach((item) => {
|
||||||
|
item.show = !hiddenKeys.has(item.key);
|
||||||
|
item.more = false;
|
||||||
|
});
|
||||||
|
nextTick(() => measureRows())
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 展示更多搜索项
|
||||||
|
function moreOptions () {
|
||||||
|
showTagCount.value = showTagCount.value === 5 ? 100 : 5
|
||||||
|
nextTick(() => measureRows())
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 有分类id就根据id搜索
|
||||||
|
if (route.query.categoryId) {
|
||||||
|
let cateId = route.query.categoryId.split(',')
|
||||||
|
Object.assign(params, route.query)
|
||||||
|
params.categoryId = cateId[cateId.length - 1]
|
||||||
|
} else {
|
||||||
|
Object.assign(params, route.query)
|
||||||
|
}
|
||||||
|
getFilterList(params);
|
||||||
|
getNav();
|
||||||
|
window.addEventListener('resize', measureRows)
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.removeEventListener('resize', measureRows)
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -27,49 +27,53 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
import { computed, useSlots } from "vue";
|
||||||
name: "UserCenterLayout",
|
import { useRouter } from "vue-router";
|
||||||
props: {
|
|
||||||
title: {
|
defineOptions({ name: "UserCenterLayout" });
|
||||||
type: String,
|
|
||||||
default: "",
|
const props = defineProps({
|
||||||
},
|
title: {
|
||||||
tabs: {
|
type: String,
|
||||||
type: Array,
|
default: "",
|
||||||
default: () => [],
|
|
||||||
},
|
|
||||||
activeTab: {
|
|
||||||
type: Number,
|
|
||||||
default: 0,
|
|
||||||
},
|
|
||||||
moreText: {
|
|
||||||
type: String,
|
|
||||||
default: "",
|
|
||||||
},
|
|
||||||
moreTo: {
|
|
||||||
type: [String, Object],
|
|
||||||
default: "",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
emits: ["tab-change", "more"],
|
tabs: {
|
||||||
computed: {
|
type: Array,
|
||||||
showToolbar() {
|
default: () => [],
|
||||||
return this.tabs.length || this.moreText || this.$slots.extra;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
methods: {
|
activeTab: {
|
||||||
handleMore() {
|
type: Number,
|
||||||
this.$emit("more");
|
default: 0,
|
||||||
if (!this.moreTo) return;
|
|
||||||
if (typeof this.moreTo === "string") {
|
|
||||||
this.$router.push(this.moreTo);
|
|
||||||
} else {
|
|
||||||
this.$router.push(this.moreTo);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
moreText: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
moreTo: {
|
||||||
|
type: [String, Object],
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(["tab-change", "more"]);
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const slots = useSlots();
|
||||||
|
|
||||||
|
const showToolbar = computed(() => {
|
||||||
|
return props.tabs.length || props.moreText || slots.extra;
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleMore() {
|
||||||
|
emit("more");
|
||||||
|
if (!props.moreTo) return;
|
||||||
|
if (typeof props.moreTo === "string") {
|
||||||
|
router.push(props.moreTo);
|
||||||
|
} else {
|
||||||
|
router.push(props.moreTo);
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -14,117 +14,112 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
|
import { ref, watch } from 'vue';
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
import { Refresh, ArrowRight } from '@element-plus/icons-vue';
|
import { Refresh, ArrowRight } from '@element-plus/icons-vue';
|
||||||
import { getVerifyImg, postVerifyImg } from './verify.js';
|
import { getVerifyImg, postVerifyImg } from './verify.js';
|
||||||
export default {
|
|
||||||
components: { Refresh, ArrowRight },
|
|
||||||
props: {
|
|
||||||
// 传入数据,判断是登录、注册、修改密码
|
|
||||||
verifyType: {
|
|
||||||
defalut: 'LOGIN',
|
|
||||||
type: String
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
show: false, // 验证码显隐
|
|
||||||
type: 'LOGIN', // 请求类型
|
|
||||||
data: { // 验证码数据
|
|
||||||
backImage: '',
|
|
||||||
slidingImage: '',
|
|
||||||
originalHeight: 150,
|
|
||||||
originalWidth: 300,
|
|
||||||
sliderWidth: 60,
|
|
||||||
sliderHeight: 60
|
|
||||||
},
|
|
||||||
distance: 0, // 拼图移动距离
|
|
||||||
flag: false, // 判断滑块是否按下
|
|
||||||
downX: 0, // 鼠标按下位置
|
|
||||||
bgColor: '#04ad11', // 滑动背景颜色
|
|
||||||
verifyText: '拖动滑块解锁' // 文字提示
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// 鼠标按下事件,开始拖动滑块
|
|
||||||
mouseDown (e) {
|
|
||||||
this.downX = e.clientX;
|
|
||||||
this.flag = true;
|
|
||||||
},
|
|
||||||
// 鼠标移动事件,计算距离
|
|
||||||
mouseMove (e) {
|
|
||||||
if (this.flag) {
|
|
||||||
let offset = e.clientX - this.downX;
|
|
||||||
|
|
||||||
if (offset > this.data.originalWidth - 43) {
|
const props = defineProps({
|
||||||
this.distance = this.data.originalWidth - 43;
|
// 传入数据,判断是登录、注册、修改密码
|
||||||
} else if (offset < 0) {
|
verifyType: {
|
||||||
this.distance = 0;
|
defalut: 'LOGIN',
|
||||||
} else {
|
type: String
|
||||||
this.distance = offset;
|
}
|
||||||
}
|
});
|
||||||
}
|
|
||||||
},
|
|
||||||
// 鼠标抬起事件,验证是否正确
|
|
||||||
mouseUp () {
|
|
||||||
if (!this.flag) return false;
|
|
||||||
this.flag = false;
|
|
||||||
let params = {
|
|
||||||
verificationEnums: this.type,
|
|
||||||
xPos: this.distance
|
|
||||||
};
|
|
||||||
postVerifyImg(params).then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
if (res.result) {
|
|
||||||
this.bgColor = 'green';
|
|
||||||
this.verifyText = '解锁成功';
|
|
||||||
this.$emit('change', { status: true, distance: this.distance });
|
|
||||||
} else {
|
|
||||||
this.bgColor = 'red';
|
|
||||||
this.verifyText = '解锁失败';
|
|
||||||
let that = this;
|
|
||||||
setTimeout(() => {
|
|
||||||
that.init();
|
|
||||||
}, 1000);
|
|
||||||
this.$emit('change', { status: false, distance: this.distance });
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.init()
|
|
||||||
}
|
|
||||||
|
|
||||||
}).catch(()=>{
|
const emit = defineEmits(['change']);
|
||||||
this.init()
|
|
||||||
});
|
const show = ref(false); // 验证码显隐
|
||||||
},
|
const type = ref('LOGIN'); // 请求类型
|
||||||
close () {
|
const data = ref({ // 验证码数据
|
||||||
this.show = false;
|
backImage: '',
|
||||||
},
|
slidingImage: '',
|
||||||
init () { // 初始化数据
|
originalHeight: 150,
|
||||||
this.flag = false;
|
originalWidth: 300,
|
||||||
this.downX = 0;
|
sliderWidth: 60,
|
||||||
this.distance = 0;
|
sliderHeight: 60
|
||||||
this.bgColor = '#04ad11';
|
});
|
||||||
this.verifyText = '拖动滑块解锁';
|
const distance = ref(0); // 拼图移动距离
|
||||||
getVerifyImg(this.type).then(res => {
|
const flag = ref(false); // 判断滑块是否按下
|
||||||
if (res.result) {
|
const downX = ref(0); // 鼠标按下位置
|
||||||
this.data = res.result;
|
const bgColor = ref('#04ad11'); // 滑动背景颜色
|
||||||
this.show = true;
|
const verifyText = ref('拖动滑块解锁'); // 文字提示
|
||||||
} else {
|
|
||||||
Message.warning('请求失败请重试!')
|
// 鼠标按下事件,开始拖动滑块
|
||||||
}
|
function mouseDown (e) {
|
||||||
});
|
downX.value = e.clientX;
|
||||||
}
|
flag.value = true;
|
||||||
},
|
}
|
||||||
watch: {
|
// 鼠标移动事件,计算距离
|
||||||
verifyType: {
|
function mouseMove (e) {
|
||||||
immediate: true,
|
if (flag.value) {
|
||||||
handler: function (v) {
|
let offset = e.clientX - downX.value;
|
||||||
this.type = v;
|
|
||||||
}
|
if (offset > data.value.originalWidth - 43) {
|
||||||
|
distance.value = data.value.originalWidth - 43;
|
||||||
|
} else if (offset < 0) {
|
||||||
|
distance.value = 0;
|
||||||
|
} else {
|
||||||
|
distance.value = offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
// 鼠标抬起事件,验证是否正确
|
||||||
|
function mouseUp () {
|
||||||
|
if (!flag.value) return false;
|
||||||
|
flag.value = false;
|
||||||
|
let params = {
|
||||||
|
verificationEnums: type.value,
|
||||||
|
xPos: distance.value
|
||||||
|
};
|
||||||
|
postVerifyImg(params).then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
if (res.result) {
|
||||||
|
bgColor.value = 'green';
|
||||||
|
verifyText.value = '解锁成功';
|
||||||
|
emit('change', { status: true, distance: distance.value });
|
||||||
|
} else {
|
||||||
|
bgColor.value = 'red';
|
||||||
|
verifyText.value = '解锁失败';
|
||||||
|
setTimeout(() => {
|
||||||
|
init();
|
||||||
|
}, 1000);
|
||||||
|
emit('change', { status: false, distance: distance.value });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
init()
|
||||||
|
}
|
||||||
|
|
||||||
|
}).catch(()=>{
|
||||||
|
init()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function close () {
|
||||||
|
show.value = false;
|
||||||
|
}
|
||||||
|
function init () { // 初始化数据
|
||||||
|
flag.value = false;
|
||||||
|
downX.value = 0;
|
||||||
|
distance.value = 0;
|
||||||
|
bgColor.value = '#04ad11';
|
||||||
|
verifyText.value = '拖动滑块解锁';
|
||||||
|
getVerifyImg(type.value).then(res => {
|
||||||
|
if (res.result) {
|
||||||
|
data.value = res.result;
|
||||||
|
show.value = true;
|
||||||
|
} else {
|
||||||
|
Message.warning('请求失败请重试!')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 父组件通过 ref 调用 init/close
|
||||||
|
defineExpose({ init, close });
|
||||||
|
|
||||||
|
watch(() => props.verifyType, (v) => {
|
||||||
|
type.value = v;
|
||||||
|
}, { immediate: true });
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.verify-content{
|
.verify-content{
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { createApp } from "vue";
|
import { createApp } from "vue";
|
||||||
|
import { createPinia } from "pinia";
|
||||||
import "core-js/stable";
|
import "core-js/stable";
|
||||||
import "./assets/styles/global-layout.scss";
|
import "./assets/styles/global-layout.scss";
|
||||||
import "./assets/styles/theme.less";
|
import "./assets/styles/theme.less";
|
||||||
import App from "./App.vue";
|
import App from "./App.vue";
|
||||||
import { router } from "./router/index";
|
import { router } from "./router/index";
|
||||||
import store from "@/vuex/store";
|
|
||||||
import { setupElementPlus } from "@/plugins/element";
|
import { setupElementPlus } from "@/plugins/element";
|
||||||
import { setupLegacyMessage } from "@/utils/message";
|
import { setupLegacyMessage } from "@/utils/message";
|
||||||
import { registerGlobalComponents } from "@/components/global.js";
|
import { registerGlobalComponents } from "@/components/global.js";
|
||||||
@@ -30,7 +30,7 @@ setupLegacyMessage(app);
|
|||||||
registerGlobalComponents(app);
|
registerGlobalComponents(app);
|
||||||
|
|
||||||
app.use(router);
|
app.use(router);
|
||||||
app.use(store);
|
app.use(createPinia());
|
||||||
|
|
||||||
app.config.globalProperties.$filters = filters;
|
app.config.globalProperties.$filters = filters;
|
||||||
app.config.globalProperties.Cookies = storage;
|
app.config.globalProperties.Cookies = storage;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<!-- LOGO 搜索 -->
|
<!-- LOGO 搜索 -->
|
||||||
<div class="width_1200 logo">
|
<div class="width_1200 logo">
|
||||||
<div>
|
<div>
|
||||||
<router-link to="/"><img :src="$store.state.logoImg" /></router-link>
|
<router-link to="/"><img :src="logoImg" /></router-link>
|
||||||
<div>
|
<div>
|
||||||
购物车(<span>{{ goodsTotal }}</span>)
|
购物车(<span>{{ goodsTotal }}</span>)
|
||||||
</div>
|
</div>
|
||||||
@@ -242,264 +242,266 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { Message, Modal } from "@/utils/message";
|
|
||||||
import Promotion from "@/components/goodsDetail/Promotion";
|
|
||||||
import * as APICart from "@/api/cart";
|
|
||||||
import * as APIMember from "@/api/member";
|
|
||||||
import { isECoupon } from "@/constants/goodsType";
|
|
||||||
export default {
|
export default {
|
||||||
name: "Cart",
|
name: "Cart",
|
||||||
beforeRouteEnter(to, from, next) {
|
beforeRouteEnter(to, from, next) {
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
next();
|
next();
|
||||||
},
|
},
|
||||||
components: {
|
};
|
||||||
Promotion,
|
</script>
|
||||||
},
|
|
||||||
data() {
|
<script setup>
|
||||||
return {
|
import { computed, onMounted, ref } from "vue";
|
||||||
couponAvailable: false, // 展示优惠券
|
import { useRouter } from "vue-router";
|
||||||
stepIndex: 0, // 当前处于哪一步,购物车==0,填写订单信息==1,成功提交订单==2
|
import { Message, Modal } from "@/utils/message";
|
||||||
goodsTotal: 0, // 商品数量
|
import Promotion from "@/components/goodsDetail/Promotion";
|
||||||
checkedNum: 0, // 选中数量
|
import * as APICart from "@/api/cart";
|
||||||
allChecked: false, // 全选
|
import * as APIMember from "@/api/member";
|
||||||
loading: false, // 加载状态
|
import { isECoupon } from "@/constants/goodsType";
|
||||||
cartList: [], // 购物车列表
|
import storage from "@/plugins/storage.js";
|
||||||
couponList: [], // 优惠券列表
|
import { useAppStore } from "@/stores/app";
|
||||||
priceDetailDTO: {}, // 价格明细
|
|
||||||
skuList: [], // sku列表
|
const router = useRouter();
|
||||||
};
|
const appStore = useAppStore();
|
||||||
},
|
|
||||||
methods: {
|
const logoImg = computed(() => appStore.logoImg);
|
||||||
// 跳转商品详情
|
|
||||||
goGoodsDetail(skuId, goodsId) {
|
const couponAvailable = ref(false); // 展示优惠券
|
||||||
let routeUrl = this.$router.resolve({
|
const stepIndex = ref(0); // 当前处于哪一步,购物车==0,填写订单信息==1,成功提交订单==2
|
||||||
path: "/goodsDetail",
|
const goodsTotal = ref(0); // 商品数量
|
||||||
query: { skuId, goodsId },
|
const checkedNum = ref(0); // 选中数量
|
||||||
});
|
const allChecked = ref(false); // 全选
|
||||||
window.open(routeUrl.href, "_blank");
|
const loading = ref(false); // 加载状态
|
||||||
},
|
const cartList = ref([]); // 购物车列表
|
||||||
// 跳转店铺首页
|
const couponList = ref([]); // 优惠券列表
|
||||||
goShopPage(id) {
|
const priceDetailDTO = ref({}); // 价格明细
|
||||||
this.$router.push({
|
const skuList = ref([]); // sku列表
|
||||||
path: "/merchant",
|
|
||||||
query: { id },
|
// 跳转商品详情
|
||||||
});
|
function goGoodsDetail(skuId, goodsId) {
|
||||||
},
|
let routeUrl = router.resolve({
|
||||||
// 同步购物车商品收藏状态
|
path: "/goodsDetail",
|
||||||
async syncCartCollectionStatus() {
|
query: { skuId, goodsId },
|
||||||
if (!this.Cookies.getItem("userInfo") || !this.cartList.length) return;
|
});
|
||||||
const skuIds = [];
|
window.open(routeUrl.href, "_blank");
|
||||||
this.cartList.forEach((shop) => {
|
}
|
||||||
(shop.skuList || []).forEach((goods) => {
|
// 跳转店铺首页
|
||||||
goods.isCollected = false;
|
function goShopPage(id) {
|
||||||
if (goods.goodsSku && goods.goodsSku.id) {
|
router.push({
|
||||||
skuIds.push(goods.goodsSku.id);
|
path: "/merchant",
|
||||||
}
|
query: { id },
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
const uniqueSkuIds = [...new Set(skuIds)];
|
// 同步购物车商品收藏状态
|
||||||
await Promise.all(
|
async function syncCartCollectionStatus() {
|
||||||
uniqueSkuIds.map(async (skuId) => {
|
if (!storage.getItem("userInfo") || !cartList.value.length) return;
|
||||||
try {
|
const skuIds = [];
|
||||||
const res = await APIMember.isCollection("GOODS", skuId);
|
cartList.value.forEach((shop) => {
|
||||||
if (res.success && res.result) {
|
(shop.skuList || []).forEach((goods) => {
|
||||||
this.cartList.forEach((shop) => {
|
goods.isCollected = false;
|
||||||
(shop.skuList || []).forEach((goods) => {
|
if (goods.goodsSku && goods.goodsSku.id) {
|
||||||
if (goods.goodsSku && goods.goodsSku.id === skuId) {
|
skuIds.push(goods.goodsSku.id);
|
||||||
goods.isCollected = true;
|
}
|
||||||
}
|
});
|
||||||
});
|
});
|
||||||
});
|
const uniqueSkuIds = [...new Set(skuIds)];
|
||||||
}
|
await Promise.all(
|
||||||
} catch (e) {
|
uniqueSkuIds.map(async (skuId) => {
|
||||||
// 单个查询失败不影响其他商品
|
try {
|
||||||
}
|
const res = await APIMember.isCollection("GOODS", skuId);
|
||||||
})
|
if (res.success && res.result) {
|
||||||
);
|
cartList.value.forEach((shop) => {
|
||||||
this.$forceUpdate();
|
(shop.skuList || []).forEach((goods) => {
|
||||||
},
|
if (goods.goodsSku && goods.goodsSku.id === skuId) {
|
||||||
// 收藏 / 取消收藏商品
|
goods.isCollected = true;
|
||||||
handleCollect(goods) {
|
|
||||||
const skuId = goods.goodsSku && goods.goodsSku.id;
|
|
||||||
if (!skuId) return;
|
|
||||||
if (goods.isCollected) {
|
|
||||||
Modal.confirm({
|
|
||||||
title: "取消收藏",
|
|
||||||
content: "确定取消收藏该商品吗?",
|
|
||||||
onOk: () => {
|
|
||||||
return APIMember.cancelCollect("GOODS", skuId).then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
goods.isCollected = false;
|
|
||||||
Message.success("取消收藏成功");
|
|
||||||
this.$forceUpdate();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
|
||||||
onCancel: () => {},
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Modal.confirm({
|
|
||||||
title: "收藏",
|
|
||||||
content: "商品收藏后可在个人中心我的收藏查看",
|
|
||||||
onOk: () => {
|
|
||||||
return APIMember.collectGoods("GOODS", skuId).then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
goods.isCollected = true;
|
|
||||||
Message.success("收藏商品成功");
|
|
||||||
this.$forceUpdate();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
|
||||||
onCancel: () => {},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 删除商品
|
|
||||||
delGoods(id) {
|
|
||||||
const idArr = [];
|
|
||||||
if (!id) {
|
|
||||||
const list = this.cartList;
|
|
||||||
list.forEach((shop) => {
|
|
||||||
shop.skuList.forEach((goods) => {
|
|
||||||
if (goods.checked) {
|
|
||||||
idArr.push(goods.goodsSku.id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
idArr.push(id);
|
|
||||||
}
|
|
||||||
Modal.confirm({
|
|
||||||
title: "删除",
|
|
||||||
content: "确定要删除该商品吗?",
|
|
||||||
onOk: () => {
|
|
||||||
APICart.delCartGoods({ skuIds: idArr.toString() }).then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
Message.success("删除成功");
|
|
||||||
this.getCartList();
|
|
||||||
} else {
|
|
||||||
Message.error(res.message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 清空购物车
|
|
||||||
clearCart() {
|
|
||||||
Modal.confirm({
|
|
||||||
title: "提示",
|
|
||||||
content: "确定要清空购物车吗?清空后不可恢复",
|
|
||||||
onOk: () => {
|
|
||||||
APICart.clearCart().then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
Message.success("清空购物车成功");
|
|
||||||
this.getCartList();
|
|
||||||
} else {
|
|
||||||
Message.error(res.message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 跳转支付页面
|
|
||||||
pay() {
|
|
||||||
if (!this.checkedNum) {
|
|
||||||
Message.warning("请至少选择一件商品");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const hasECoupon = this.cartList.some((shop) =>
|
|
||||||
(shop.skuList || []).some(
|
|
||||||
(goods) => goods.checked && isECoupon(goods.goodsSku && goods.goodsSku.goodsType)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
if (hasECoupon) {
|
|
||||||
Message.warning("电子卡券请使用立即购买,不可通过购物车结算");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.$router.push({ path: "/pay", query: { way: "CART" } });
|
|
||||||
},
|
|
||||||
// 展示优惠券
|
|
||||||
showCoupon(storeId, index) {
|
|
||||||
this.couponAvailable = index;
|
|
||||||
},
|
|
||||||
// 设置购买数量
|
|
||||||
changeNum(val, id) {
|
|
||||||
if (val) {
|
|
||||||
APICart.setCartGoodsNum({ skuId: id, num: val }).then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
this.getCartList();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 设置商品选中状态
|
|
||||||
async changeChecked(status, type, id) {
|
|
||||||
const check = status ? 1 : 0;
|
|
||||||
if (type === "all") {
|
|
||||||
// 全选
|
|
||||||
await APICart.setCheckedAll({ checked: check });
|
|
||||||
} else if (type === "shop") {
|
|
||||||
// 选中店铺所有商品
|
|
||||||
await APICart.setCheckedSeller({ checked: check, storeId: id });
|
|
||||||
} else {
|
|
||||||
// 单个商品
|
|
||||||
await APICart.setCheckedGoods({ checked: check, skuId: id });
|
|
||||||
}
|
|
||||||
|
|
||||||
this.getCartList();
|
|
||||||
},
|
|
||||||
// 领取优惠券
|
|
||||||
async receiveShopCoupon(item) {
|
|
||||||
let res = await APIMember.receiveCoupon(item.id);
|
|
||||||
if (res.success) {
|
|
||||||
item["disabled"] = true;
|
|
||||||
Message.success("领取成功");
|
|
||||||
} else {
|
|
||||||
Message.error(res.message);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 购物车列表
|
|
||||||
async getCartList() {
|
|
||||||
this.loading = true;
|
|
||||||
try {
|
|
||||||
let res = await APICart.cartGoodsAll();
|
|
||||||
this.loading = false;
|
|
||||||
if (res.success) {
|
|
||||||
this.cartList = res.result.cartList;
|
|
||||||
this.priceDetailDTO = res.result.priceDetailDTO;
|
|
||||||
this.skuList = res.result.skuList;
|
|
||||||
this.checkedNum = 0;
|
|
||||||
let allChecked = true;
|
|
||||||
for (let k = 0; k < this.cartList.length; k++) {
|
|
||||||
let shop = this.cartList[k];
|
|
||||||
let list = await APIMember.couponList({ storeId: shop.storeId });
|
|
||||||
shop.couponList.push(...list.result.records);
|
|
||||||
}
|
|
||||||
for (let i = 0; i < this.skuList.length; i++) {
|
|
||||||
if (this.skuList[i].checked) {
|
|
||||||
this.checkedNum += this.skuList[i].num;
|
|
||||||
} else {
|
|
||||||
allChecked = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.$forceUpdate();
|
|
||||||
this.allChecked = allChecked;
|
|
||||||
this.syncCartCollectionStatus();
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (e) {
|
||||||
this.loading = false;
|
// 单个查询失败不影响其他商品
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
},
|
);
|
||||||
mounted() {
|
}
|
||||||
this.getCartList();
|
// 收藏 / 取消收藏商品
|
||||||
APICart.cartCount().then((res) => {
|
function handleCollect(goods) {
|
||||||
// 购物车商品数量
|
const skuId = goods.goodsSku && goods.goodsSku.id;
|
||||||
if (res.success) this.goodsTotal = res.result;
|
if (!skuId) return;
|
||||||
|
if (goods.isCollected) {
|
||||||
|
Modal.confirm({
|
||||||
|
title: "取消收藏",
|
||||||
|
content: "确定取消收藏该商品吗?",
|
||||||
|
onOk: () => {
|
||||||
|
return APIMember.cancelCollect("GOODS", skuId).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
goods.isCollected = false;
|
||||||
|
Message.success("取消收藏成功");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onCancel: () => {},
|
||||||
});
|
});
|
||||||
},
|
return;
|
||||||
};
|
}
|
||||||
|
Modal.confirm({
|
||||||
|
title: "收藏",
|
||||||
|
content: "商品收藏后可在个人中心我的收藏查看",
|
||||||
|
onOk: () => {
|
||||||
|
return APIMember.collectGoods("GOODS", skuId).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
goods.isCollected = true;
|
||||||
|
Message.success("收藏商品成功");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onCancel: () => {},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 删除商品
|
||||||
|
function delGoods(id) {
|
||||||
|
const idArr = [];
|
||||||
|
if (!id) {
|
||||||
|
const list = cartList.value;
|
||||||
|
list.forEach((shop) => {
|
||||||
|
shop.skuList.forEach((goods) => {
|
||||||
|
if (goods.checked) {
|
||||||
|
idArr.push(goods.goodsSku.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
idArr.push(id);
|
||||||
|
}
|
||||||
|
Modal.confirm({
|
||||||
|
title: "删除",
|
||||||
|
content: "确定要删除该商品吗?",
|
||||||
|
onOk: () => {
|
||||||
|
APICart.delCartGoods({ skuIds: idArr.toString() }).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
Message.success("删除成功");
|
||||||
|
getCartList();
|
||||||
|
} else {
|
||||||
|
Message.error(res.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 清空购物车
|
||||||
|
function clearCart() {
|
||||||
|
Modal.confirm({
|
||||||
|
title: "提示",
|
||||||
|
content: "确定要清空购物车吗?清空后不可恢复",
|
||||||
|
onOk: () => {
|
||||||
|
APICart.clearCart().then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
Message.success("清空购物车成功");
|
||||||
|
getCartList();
|
||||||
|
} else {
|
||||||
|
Message.error(res.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 跳转支付页面
|
||||||
|
function pay() {
|
||||||
|
if (!checkedNum.value) {
|
||||||
|
Message.warning("请至少选择一件商品");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const hasECoupon = cartList.value.some((shop) =>
|
||||||
|
(shop.skuList || []).some(
|
||||||
|
(goods) => goods.checked && isECoupon(goods.goodsSku && goods.goodsSku.goodsType)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if (hasECoupon) {
|
||||||
|
Message.warning("电子卡券请使用立即购买,不可通过购物车结算");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
router.push({ path: "/pay", query: { way: "CART" } });
|
||||||
|
}
|
||||||
|
// 展示优惠券
|
||||||
|
function showCoupon(storeId, index) {
|
||||||
|
couponAvailable.value = index;
|
||||||
|
}
|
||||||
|
// 设置购买数量
|
||||||
|
function changeNum(val, id) {
|
||||||
|
if (val) {
|
||||||
|
APICart.setCartGoodsNum({ skuId: id, num: val }).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
getCartList();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 设置商品选中状态
|
||||||
|
async function changeChecked(status, type, id) {
|
||||||
|
const check = status ? 1 : 0;
|
||||||
|
if (type === "all") {
|
||||||
|
// 全选
|
||||||
|
await APICart.setCheckedAll({ checked: check });
|
||||||
|
} else if (type === "shop") {
|
||||||
|
// 选中店铺所有商品
|
||||||
|
await APICart.setCheckedSeller({ checked: check, storeId: id });
|
||||||
|
} else {
|
||||||
|
// 单个商品
|
||||||
|
await APICart.setCheckedGoods({ checked: check, skuId: id });
|
||||||
|
}
|
||||||
|
|
||||||
|
getCartList();
|
||||||
|
}
|
||||||
|
// 领取优惠券
|
||||||
|
async function receiveShopCoupon(item) {
|
||||||
|
let res = await APIMember.receiveCoupon(item.id);
|
||||||
|
if (res.success) {
|
||||||
|
item["disabled"] = true;
|
||||||
|
Message.success("领取成功");
|
||||||
|
} else {
|
||||||
|
Message.error(res.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 购物车列表
|
||||||
|
async function getCartList() {
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
let res = await APICart.cartGoodsAll();
|
||||||
|
loading.value = false;
|
||||||
|
if (res.success) {
|
||||||
|
cartList.value = res.result.cartList;
|
||||||
|
priceDetailDTO.value = res.result.priceDetailDTO;
|
||||||
|
skuList.value = res.result.skuList;
|
||||||
|
checkedNum.value = 0;
|
||||||
|
let allCheckedVal = true;
|
||||||
|
for (let k = 0; k < cartList.value.length; k++) {
|
||||||
|
let shop = cartList.value[k];
|
||||||
|
let list = await APIMember.couponList({ storeId: shop.storeId });
|
||||||
|
shop.couponList.push(...list.result.records);
|
||||||
|
}
|
||||||
|
for (let i = 0; i < skuList.value.length; i++) {
|
||||||
|
if (skuList.value[i].checked) {
|
||||||
|
checkedNum.value += skuList.value[i].num;
|
||||||
|
} else {
|
||||||
|
allCheckedVal = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
allChecked.value = allCheckedVal;
|
||||||
|
syncCartCollectionStatus();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getCartList();
|
||||||
|
APICart.cartCount().then((res) => {
|
||||||
|
// 购物车商品数量
|
||||||
|
if (res.success) goodsTotal.value = res.result;
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -43,126 +43,126 @@
|
|||||||
<BaseFooter></BaseFooter>
|
<BaseFooter></BaseFooter>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
|
import { onMounted, reactive, ref } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { Modal } from "@/utils/message";
|
import { Modal } from "@/utils/message";
|
||||||
import storage from "@/plugins/storage";
|
import storage from "@/plugins/storage";
|
||||||
import { couponList, receiveCoupon } from "@/api/member.js";
|
import { couponList, receiveCoupon } from "@/api/member.js";
|
||||||
export default {
|
|
||||||
data() {
|
const route = useRoute();
|
||||||
return {
|
const router = useRouter();
|
||||||
list: [], // 优惠券列表
|
|
||||||
total: 0, // 优惠券总数
|
const list = ref([]); // 优惠券列表
|
||||||
loading: false,
|
const total = ref(0); // 优惠券总数
|
||||||
params: {
|
const loading = ref(false);
|
||||||
// 请求参数
|
const params = reactive({
|
||||||
getType: "FREE",
|
// 请求参数
|
||||||
pageNumber: 1,
|
getType: "FREE",
|
||||||
pageSize: 20,
|
pageNumber: 1,
|
||||||
},
|
pageSize: 20,
|
||||||
};
|
});
|
||||||
},
|
|
||||||
methods: {
|
function getList() {
|
||||||
getList() {
|
if (loading.value) return;
|
||||||
if (this.loading) return;
|
loading.value = true;
|
||||||
this.loading = true;
|
couponList(params)
|
||||||
couponList(this.params)
|
.then((res) => {
|
||||||
.then((res) => {
|
if (res && res.success && res.result) {
|
||||||
if (res && res.success && res.result) {
|
list.value = res.result.records || [];
|
||||||
this.list = res.result.records || [];
|
total.value = res.result.total || 0;
|
||||||
this.total = res.result.total || 0;
|
} else {
|
||||||
} else {
|
list.value = [];
|
||||||
this.list = [];
|
total.value = 0;
|
||||||
this.total = 0;
|
}
|
||||||
}
|
})
|
||||||
})
|
.catch(() => {
|
||||||
.catch(() => {
|
list.value = [];
|
||||||
this.list = [];
|
total.value = 0;
|
||||||
this.total = 0;
|
})
|
||||||
})
|
.finally(() => {
|
||||||
.finally(() => {
|
loading.value = false;
|
||||||
this.loading = false;
|
});
|
||||||
});
|
}
|
||||||
},
|
// 分页 改变页码
|
||||||
// 分页 改变页码
|
function changePageNum(val) {
|
||||||
changePageNum(val) {
|
params.pageNumber = val;
|
||||||
this.params.pageNumber = val;
|
getList();
|
||||||
this.getList();
|
}
|
||||||
},
|
// 分页 改变每页数
|
||||||
// 分页 改变每页数
|
function changePageSize(val) {
|
||||||
changePageSize(val) {
|
params.pageNumber = 1;
|
||||||
this.params.pageNumber = 1;
|
params.pageSize = val;
|
||||||
this.params.pageSize = val;
|
getList();
|
||||||
this.getList();
|
}
|
||||||
},
|
// 领取优惠券
|
||||||
// 领取优惠券
|
function receive(item) {
|
||||||
receive(item) {
|
if (isFinished(item)) return;
|
||||||
if (this.isFinished(item)) return;
|
const userInfo = storage.getItem("userInfo");
|
||||||
const userInfo = storage.getItem("userInfo");
|
const isLogin = userInfo && JSON.parse(userInfo).username;
|
||||||
const isLogin = userInfo && JSON.parse(userInfo).username;
|
if (!isLogin) {
|
||||||
if (!isLogin) {
|
Modal.confirm({
|
||||||
Modal.confirm({
|
title: "温馨提示",
|
||||||
title: "温馨提示",
|
content: "请登录后执行此操作",
|
||||||
content: "请登录后执行此操作",
|
okText: "立即登录",
|
||||||
okText: "立即登录",
|
cancelText: "取消",
|
||||||
cancelText: "取消",
|
onOk: () => {
|
||||||
onOk: () => {
|
router.push({
|
||||||
this.$router.push({
|
path: "/login",
|
||||||
path: "/login",
|
query: {
|
||||||
query: {
|
rePath: route.path,
|
||||||
rePath: this.$route.path,
|
query: JSON.stringify(route.query),
|
||||||
query: JSON.stringify(this.$route.query),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return;
|
},
|
||||||
}
|
});
|
||||||
receiveCoupon(item.id)
|
return;
|
||||||
.then((res) => {
|
}
|
||||||
if (!res || !res.success) return;
|
receiveCoupon(item.id)
|
||||||
Modal.confirm({
|
.then((res) => {
|
||||||
title: "领取优惠券",
|
if (!res || !res.success) return;
|
||||||
content: "优惠券领取成功,可到我的优惠券页面查看",
|
Modal.confirm({
|
||||||
okText: "我的优惠券",
|
title: "领取优惠券",
|
||||||
cancelText: "立即使用",
|
content: "优惠券领取成功,可到我的优惠券页面查看",
|
||||||
closable: true,
|
okText: "我的优惠券",
|
||||||
onOk: () => {
|
cancelText: "立即使用",
|
||||||
this.$router.push("/home/Coupons");
|
closable: true,
|
||||||
},
|
onOk: () => {
|
||||||
onCancel: () => {
|
router.push("/home/Coupons");
|
||||||
this.$router.push({
|
},
|
||||||
path: "/goodsList",
|
onCancel: () => {
|
||||||
query: { promotionsId: item.id, promotionType: "COUPON" },
|
router.push({
|
||||||
});
|
path: "/goodsList",
|
||||||
},
|
query: { promotionsId: item.id, promotionType: "COUPON" },
|
||||||
});
|
});
|
||||||
})
|
},
|
||||||
.catch((err) => {
|
});
|
||||||
console.warn('领取优惠券失败', err);
|
})
|
||||||
});
|
.catch((err) => {
|
||||||
},
|
console.warn('领取优惠券失败', err);
|
||||||
// 发行量已领完
|
});
|
||||||
isFinished(item) {
|
}
|
||||||
return Number(item.publishNum) > 0 && Number(item.receivedNum) >= Number(item.publishNum);
|
// 发行量已领完
|
||||||
},
|
function isFinished(item) {
|
||||||
// 使用门槛文案
|
return Number(item.publishNum) > 0 && Number(item.receivedNum) >= Number(item.publishNum);
|
||||||
thresholdText(item) {
|
}
|
||||||
const threshold = Number(item.consumeThreshold);
|
// 使用门槛文案
|
||||||
if (!threshold) return "无使用门槛";
|
function thresholdText(item) {
|
||||||
return `满${item.consumeThreshold}元可用`;
|
const threshold = Number(item.consumeThreshold);
|
||||||
},
|
if (!threshold) return "无使用门槛";
|
||||||
// 券范围。店铺名为 platform 时是平台通用券
|
return `满${item.consumeThreshold}元可用`;
|
||||||
scopeTag(type, storeName) {
|
}
|
||||||
if (type === "PORTION_GOODS") return "商品券";
|
// 券范围。店铺名为 platform 时是平台通用券
|
||||||
if (type === "PORTION_GOODS_CATEGORY") return "分类券";
|
function scopeTag(type, storeName) {
|
||||||
if (storeName && storeName !== "platform") return "店铺券";
|
if (type === "PORTION_GOODS") return "商品券";
|
||||||
return "通用券";
|
if (type === "PORTION_GOODS_CATEGORY") return "分类券";
|
||||||
},
|
if (storeName && storeName !== "platform") return "店铺券";
|
||||||
},
|
return "通用券";
|
||||||
mounted() {
|
}
|
||||||
this.getList();
|
|
||||||
},
|
onMounted(() => {
|
||||||
};
|
getList();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "../assets/styles/coupon.scss";
|
@import "../assets/styles/coupon.scss";
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<!-- 顶部logo -->
|
<!-- 顶部logo -->
|
||||||
<div class="logo-box">
|
<div class="logo-box">
|
||||||
<img
|
<img
|
||||||
:src="$store.state.logoImg" width='150'
|
:src="logoImg" width='150'
|
||||||
@click="$router.push('/')"
|
@click="$router.push('/')"
|
||||||
/>
|
/>
|
||||||
<div>修改密码</div>
|
<div>修改密码</div>
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
<div class="login-container">
|
<div class="login-container">
|
||||||
<!-- 验证手机号 -->
|
<!-- 验证手机号 -->
|
||||||
<el-form
|
<el-form
|
||||||
ref="formFirst"
|
ref="formFirstRef"
|
||||||
:model="formFirst"
|
:model="formFirst"
|
||||||
:rules="ruleInline"
|
:rules="ruleInline"
|
||||||
style="width:300px;"
|
style="width:300px;"
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<el-form
|
<el-form
|
||||||
ref="form"
|
ref="formRef"
|
||||||
:model="form"
|
:model="form"
|
||||||
:rules="ruleInline"
|
:rules="ruleInline"
|
||||||
style="width:300px;"
|
style="width:300px;"
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
</el-form>
|
</el-form>
|
||||||
<!-- 拼图验证码 -->
|
<!-- 拼图验证码 -->
|
||||||
<Verify
|
<Verify
|
||||||
ref="verify"
|
ref="verifyRef"
|
||||||
class="verify-con"
|
class="verify-con"
|
||||||
:verifyType="verifyType"
|
:verifyType="verifyType"
|
||||||
@change="verifyChange"
|
@change="verifyChange"
|
||||||
@@ -103,8 +103,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { computed, onMounted, reactive, ref } from "vue";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
import * as RegExp from '@/plugins/RegExp.js';
|
import * as RegExp from '@/plugins/RegExp.js';
|
||||||
import { md5 } from '@/plugins/md5.js';
|
import { md5 } from '@/plugins/md5.js';
|
||||||
@@ -113,143 +114,148 @@ import { sendSms } from '@/api/common.js';
|
|||||||
import Verify from '@/components/verify';
|
import Verify from '@/components/verify';
|
||||||
import { Lock, Iphone, Document } from '@element-plus/icons-vue';
|
import { Lock, Iphone, Document } from '@element-plus/icons-vue';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
export default {
|
import { useAppStore } from '@/stores/app';
|
||||||
name: 'ForgetPassword',
|
|
||||||
components: { Verify, Lock, Iphone, Document },
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
config,
|
|
||||||
loading: false, // 加载状态
|
|
||||||
loading1: false, // 第二步加载状态
|
|
||||||
formFirst: { // 手机验证码表单
|
|
||||||
// 注册表单
|
|
||||||
mobile: '',
|
|
||||||
code: ''
|
|
||||||
},
|
|
||||||
form: { // 密码
|
|
||||||
password: '',
|
|
||||||
oncePasd: ''
|
|
||||||
},
|
|
||||||
year: new Date().getFullYear(), // 当前年份
|
|
||||||
step: 0, // 步骤
|
|
||||||
ruleInline: {
|
|
||||||
// 验证规则
|
|
||||||
mobile: [
|
|
||||||
{ required: true, message: '请输入手机号码' },
|
|
||||||
{
|
|
||||||
pattern: RegExp.mobile,
|
|
||||||
trigger: 'blur',
|
|
||||||
message: '请输入正确的手机号'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
code: [{ required: true, message: '请输入手机验证码' }],
|
|
||||||
password: [{required: true, message: '密码不能为空'}, {pattern: RegExp.password, message: '密码不能少于6位'}]
|
|
||||||
},
|
|
||||||
verifyStatus: false, // 图片验证状态
|
|
||||||
verifyType: 'FIND_USER', // 图片验证类型
|
|
||||||
codeMsg: '发送验证码', // 验证码文字
|
|
||||||
interval: '', // 定时器
|
|
||||||
time: 60 // 倒计时时间
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
hideVerify() {
|
|
||||||
this.$refs.verify?.close?.();
|
|
||||||
},
|
|
||||||
// 提交短信验证码,修改密码
|
|
||||||
next () {
|
|
||||||
this.$refs.formFirst.validate((valid) => {
|
|
||||||
if (valid) {
|
|
||||||
this.loading = true;
|
|
||||||
let data = JSON.parse(JSON.stringify(this.formFirst));
|
|
||||||
apiLogin.validateCode(data).then((res) => {
|
|
||||||
this.loading = false;
|
|
||||||
if (res.success) {
|
|
||||||
// Message.success('');
|
|
||||||
this.step = 1;
|
|
||||||
} else {
|
|
||||||
Message.warning(res.message);
|
|
||||||
}
|
|
||||||
}).catch(() => { this.loading = false; });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
handleSubmit () { // 提交密码
|
|
||||||
this.$refs.form.validate(valid => {
|
|
||||||
if (valid) {
|
|
||||||
let params = JSON.parse(JSON.stringify(this.form));
|
|
||||||
if (params.password !== params.oncePasd) {
|
|
||||||
Message.warning('两次输入密码不一致');
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
params.mobile = this.formFirst.mobile;
|
|
||||||
params.password = md5(params.password);
|
|
||||||
delete params.oncePasd;
|
|
||||||
this.loading1 = true;
|
|
||||||
|
|
||||||
apiLogin.resetPassword(params).then(res => {
|
defineOptions({ name: 'ForgetPassword' });
|
||||||
this.loading1 = false;
|
|
||||||
if (res.success) {
|
const router = useRouter();
|
||||||
Message.success('修改密码成功');
|
const appStore = useAppStore();
|
||||||
this.$router.push('login');
|
const logoImg = computed(() => appStore.logoImg);
|
||||||
}
|
|
||||||
}).catch(() => { this.loading = false; });
|
const loading = ref(false); // 加载状态
|
||||||
};
|
const loading1 = ref(false); // 第二步加载状态
|
||||||
});
|
const formFirst = reactive({ // 手机验证码表单
|
||||||
},
|
// 注册表单
|
||||||
sendCode () { // 发送验证码
|
mobile: '',
|
||||||
if (this.time === 60) {
|
code: ''
|
||||||
if (this.formFirst.mobile === '') {
|
});
|
||||||
Message.warning('请先填写手机号');
|
const form = reactive({ // 密码
|
||||||
return;
|
password: '',
|
||||||
}
|
oncePasd: ''
|
||||||
if (!this.verifyStatus) {
|
});
|
||||||
Message.warning('请先完成安全验证');
|
const year = new Date().getFullYear(); // 当前年份
|
||||||
return;
|
const step = ref(0); // 步骤
|
||||||
}
|
const ruleInline = {
|
||||||
let params = {
|
// 验证规则
|
||||||
mobile: this.formFirst.mobile,
|
mobile: [
|
||||||
verificationEnums: 'FIND_USER'
|
{ required: true, message: '请输入手机号码' },
|
||||||
};
|
{
|
||||||
sendSms(params).then(res => {
|
pattern: RegExp.mobile,
|
||||||
if (res.success) {
|
trigger: 'blur',
|
||||||
Message.success('验证码发送成功');
|
message: '请输入正确的手机号'
|
||||||
let that = this;
|
|
||||||
this.interval = setInterval(() => {
|
|
||||||
that.time--;
|
|
||||||
if (that.time === 0) {
|
|
||||||
that.time = 60;
|
|
||||||
that.codeMsg = '重新发送';
|
|
||||||
that.verifyStatus = false;
|
|
||||||
clearInterval(that.interval);
|
|
||||||
} else {
|
|
||||||
that.codeMsg = that.time;
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
} else {
|
|
||||||
Message.warning(res.message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
verifyChange (con) { // 验证通过
|
|
||||||
if (!con.status) return;
|
|
||||||
this.hideVerify();
|
|
||||||
this.verifyStatus = true;
|
|
||||||
},
|
|
||||||
verifyBtnClick () {
|
|
||||||
if (!this.verifyStatus) {
|
|
||||||
this.$refs.verify?.init();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
],
|
||||||
mounted () {
|
code: [{ required: true, message: '请输入手机验证码' }],
|
||||||
document.querySelector('.forget-password').style.height = window.innerHeight + 'px'
|
password: [{required: true, message: '密码不能为空'}, {pattern: RegExp.password, message: '密码不能少于6位'}]
|
||||||
this.$refs.formFirst.resetFields();
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
const verifyStatus = ref(false); // 图片验证状态
|
||||||
|
const verifyType = ref('FIND_USER'); // 图片验证类型
|
||||||
|
const codeMsg = ref('发送验证码'); // 验证码文字
|
||||||
|
const interval = ref(''); // 定时器
|
||||||
|
const time = ref(60); // 倒计时时间
|
||||||
|
|
||||||
|
const formFirstRef = ref(null);
|
||||||
|
const formRef = ref(null);
|
||||||
|
const verifyRef = ref(null);
|
||||||
|
|
||||||
|
function hideVerify() {
|
||||||
|
verifyRef.value?.close?.();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交短信验证码,修改密码
|
||||||
|
function next () {
|
||||||
|
formFirstRef.value.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
loading.value = true;
|
||||||
|
let data = JSON.parse(JSON.stringify(formFirst));
|
||||||
|
apiLogin.validateCode(data).then((res) => {
|
||||||
|
loading.value = false;
|
||||||
|
if (res.success) {
|
||||||
|
// Message.success('');
|
||||||
|
step.value = 1;
|
||||||
|
} else {
|
||||||
|
Message.warning(res.message);
|
||||||
|
}
|
||||||
|
}).catch(() => { loading.value = false; });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSubmit () { // 提交密码
|
||||||
|
formRef.value.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
let params = JSON.parse(JSON.stringify(form));
|
||||||
|
if (params.password !== params.oncePasd) {
|
||||||
|
Message.warning('两次输入密码不一致');
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
params.mobile = formFirst.mobile;
|
||||||
|
params.password = md5(params.password);
|
||||||
|
delete params.oncePasd;
|
||||||
|
loading1.value = true;
|
||||||
|
|
||||||
|
apiLogin.resetPassword(params).then(res => {
|
||||||
|
loading1.value = false;
|
||||||
|
if (res.success) {
|
||||||
|
Message.success('修改密码成功');
|
||||||
|
router.push('login');
|
||||||
|
}
|
||||||
|
}).catch(() => { loading.value = false; });
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendCode () { // 发送验证码
|
||||||
|
if (time.value === 60) {
|
||||||
|
if (formFirst.mobile === '') {
|
||||||
|
Message.warning('请先填写手机号');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!verifyStatus.value) {
|
||||||
|
Message.warning('请先完成安全验证');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let params = {
|
||||||
|
mobile: formFirst.mobile,
|
||||||
|
verificationEnums: 'FIND_USER'
|
||||||
|
};
|
||||||
|
sendSms(params).then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
Message.success('验证码发送成功');
|
||||||
|
interval.value = setInterval(() => {
|
||||||
|
time.value--;
|
||||||
|
if (time.value === 0) {
|
||||||
|
time.value = 60;
|
||||||
|
codeMsg.value = '重新发送';
|
||||||
|
verifyStatus.value = false;
|
||||||
|
clearInterval(interval.value);
|
||||||
|
} else {
|
||||||
|
codeMsg.value = time.value;
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
} else {
|
||||||
|
Message.warning(res.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyChange (con) { // 验证通过
|
||||||
|
if (!con.status) return;
|
||||||
|
hideVerify();
|
||||||
|
verifyStatus.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyBtnClick () {
|
||||||
|
if (!verifyStatus.value) {
|
||||||
|
verifyRef.value?.init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
document.querySelector('.forget-password').style.height = window.innerHeight + 'px'
|
||||||
|
formFirstRef.value.resetFields();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -30,120 +30,120 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { Message } from "@/utils/message";
|
|
||||||
import Search from "@/components/Search";
|
|
||||||
|
|
||||||
import ShowGoods from "@/components/goodsDetail/ShowGoods";
|
|
||||||
import empty from "@/components/empty/Main";
|
|
||||||
import ShowGoodsDetail from "@/components/goodsDetail/ShowGoodsDetail";
|
|
||||||
import { goodsSkuDetail } from "@/api/goods";
|
|
||||||
import { getGoodsDistribution } from "@/api/member";
|
|
||||||
export default {
|
export default {
|
||||||
name: "GoodsDetail",
|
name: "GoodsDetail",
|
||||||
beforeRouteEnter (to, from, next) {
|
beforeRouteEnter (to, from, next) {
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
next();
|
next();
|
||||||
},
|
},
|
||||||
created () {
|
|
||||||
this.getGoodsDetail();
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
goodsMsg: {}, // 商品信息
|
|
||||||
isLoading: false, // 加载状态
|
|
||||||
categoryBar: [], // 分类
|
|
||||||
takeDownSale:false, // 是否下架
|
|
||||||
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// 跳转首页或商品页面
|
|
||||||
target(url){
|
|
||||||
this.$router.push({path: url})
|
|
||||||
|
|
||||||
},
|
|
||||||
// 点击规格
|
|
||||||
targetClickSku (val) {
|
|
||||||
this.getGoodsDetail(val);
|
|
||||||
},
|
|
||||||
// 获取商品详情
|
|
||||||
getGoodsDetail (val) {
|
|
||||||
this.isLoading = true;
|
|
||||||
const params = val || this.$route.query;
|
|
||||||
|
|
||||||
// 分销员id
|
|
||||||
let distributionId =
|
|
||||||
params && params.distributionId
|
|
||||||
? params.distributionId
|
|
||||||
: this.Cookies.getItem("distributionId");
|
|
||||||
// 如果有分销信息
|
|
||||||
if (distributionId) {
|
|
||||||
// 先存储
|
|
||||||
this.Cookies.setItem("distributionId", params.distributionId);
|
|
||||||
let _this = this;
|
|
||||||
// 绑定关系
|
|
||||||
getGoodsDistribution(params.distributionId).then((res) => {
|
|
||||||
// 绑定成功,则清除关系
|
|
||||||
if (res.success) {
|
|
||||||
_this.Cookies.removeItem("distributionId");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
goodsSkuDetail(params)
|
|
||||||
.then((res) => {
|
|
||||||
this.isLoading = false;
|
|
||||||
if (res.success) {
|
|
||||||
|
|
||||||
const result = res.result;
|
|
||||||
const cateName = res.result.categoryName;
|
|
||||||
const cateId = result.data.categoryPath.split(",");
|
|
||||||
const cateArr = [];
|
|
||||||
cateId.forEach((e, index) => {
|
|
||||||
// 插入分类id和name
|
|
||||||
cateArr.push({
|
|
||||||
id: e,
|
|
||||||
name: cateName ? cateName[index] : "",
|
|
||||||
});
|
|
||||||
});
|
|
||||||
this.categoryBar = cateArr;
|
|
||||||
this["goodsMsg"] = res.result;
|
|
||||||
if (!this.goodsMsg.data.intro) {
|
|
||||||
this.goodsMsg.data.intro = ''
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Message.error(res.message);
|
|
||||||
this.isLoading = false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
this.isLoading = false
|
|
||||||
if(e.code === 11001){
|
|
||||||
this.takeDownSale = true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
goGoodsList (currIndex) {
|
|
||||||
// 跳转商品列表
|
|
||||||
const arr = [];
|
|
||||||
this.categoryBar.forEach((e, index) => {
|
|
||||||
if (index <= currIndex) {
|
|
||||||
arr.push(e.id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
path: "/goodsList",
|
|
||||||
query: { categoryId: arr.toString() },
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
watch: {},
|
|
||||||
components: { Search,
|
|
||||||
ShowGoods,
|
|
||||||
ShowGoodsDetail,
|
|
||||||
empty },
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
|
import { Message } from "@/utils/message";
|
||||||
|
import storage from "@/plugins/storage.js";
|
||||||
|
import Search from "@/components/Search";
|
||||||
|
import ShowGoods from "@/components/goodsDetail/ShowGoods";
|
||||||
|
import empty from "@/components/empty/Main";
|
||||||
|
import ShowGoodsDetail from "@/components/goodsDetail/ShowGoodsDetail";
|
||||||
|
import { goodsSkuDetail } from "@/api/goods";
|
||||||
|
import { getGoodsDistribution } from "@/api/member";
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const goodsMsg = ref({}); // 商品信息
|
||||||
|
const isLoading = ref(false); // 加载状态
|
||||||
|
const categoryBar = ref([]); // 分类
|
||||||
|
const takeDownSale = ref(false); // 是否下架
|
||||||
|
|
||||||
|
// 跳转首页或商品页面
|
||||||
|
function target(url){
|
||||||
|
router.push({path: url})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击规格
|
||||||
|
function targetClickSku (val) {
|
||||||
|
getGoodsDetail(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取商品详情
|
||||||
|
function getGoodsDetail (val) {
|
||||||
|
isLoading.value = true;
|
||||||
|
const params = val || route.query;
|
||||||
|
|
||||||
|
// 分销员id
|
||||||
|
let distributionId =
|
||||||
|
params && params.distributionId
|
||||||
|
? params.distributionId
|
||||||
|
: storage.getItem("distributionId");
|
||||||
|
// 如果有分销信息
|
||||||
|
if (distributionId) {
|
||||||
|
// 先存储
|
||||||
|
storage.setItem("distributionId", params.distributionId);
|
||||||
|
// 绑定关系
|
||||||
|
getGoodsDistribution(params.distributionId).then((res) => {
|
||||||
|
// 绑定成功,则清除关系
|
||||||
|
if (res.success) {
|
||||||
|
storage.removeItem("distributionId");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
goodsSkuDetail(params)
|
||||||
|
.then((res) => {
|
||||||
|
isLoading.value = false;
|
||||||
|
if (res.success) {
|
||||||
|
|
||||||
|
const result = res.result;
|
||||||
|
const cateName = res.result.categoryName;
|
||||||
|
const cateId = result.data.categoryPath.split(",");
|
||||||
|
const cateArr = [];
|
||||||
|
cateId.forEach((e, index) => {
|
||||||
|
// 插入分类id和name
|
||||||
|
cateArr.push({
|
||||||
|
id: e,
|
||||||
|
name: cateName ? cateName[index] : "",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
categoryBar.value = cateArr;
|
||||||
|
goodsMsg.value = res.result;
|
||||||
|
if (!goodsMsg.value.data.intro) {
|
||||||
|
goodsMsg.value.data.intro = ''
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Message.error(res.message);
|
||||||
|
isLoading.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
isLoading.value = false
|
||||||
|
if(e.code === 11001){
|
||||||
|
takeDownSale.value = true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function goGoodsList (currIndex) {
|
||||||
|
// 跳转商品列表
|
||||||
|
const arr = [];
|
||||||
|
categoryBar.value.forEach((e, index) => {
|
||||||
|
if (index <= currIndex) {
|
||||||
|
arr.push(e.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
path: "/goodsList",
|
||||||
|
query: { categoryId: arr.toString() },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化(原 created 钩子逻辑)
|
||||||
|
getGoodsDetail();
|
||||||
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.goods-detail-page {
|
.goods-detail-page {
|
||||||
background: #f5f5f5;
|
background: #f5f5f5;
|
||||||
|
|||||||
@@ -104,199 +104,207 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { Message } from "@/utils/message";
|
|
||||||
import { ArrowDown, ArrowUp } from '@element-plus/icons-vue';
|
|
||||||
import GoodsClassNav from "@/components/nav/GoodsClassNav";
|
|
||||||
import GoodsThumb from "@/components/goods/GoodsThumb.vue";
|
|
||||||
import * as apiGoods from "@/api/goods";
|
|
||||||
export default {
|
export default {
|
||||||
name: "GoodsList",
|
name: "GoodsList",
|
||||||
beforeRouteEnter(to, from, next) {
|
beforeRouteEnter(to, from, next) {
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
next();
|
next();
|
||||||
},
|
},
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
sortIndex: 0, // 排序状态
|
|
||||||
priceSortIndex: 4,
|
|
||||||
sortPriceIndex: false, // 判断价格升序还是降序
|
|
||||||
searched: false,
|
|
||||||
recommendList: [],
|
|
||||||
goodsTool: [
|
|
||||||
// 排序类型
|
|
||||||
{ title: "综合", en: "" },
|
|
||||||
{ title: "销量", en: "buyCount" },
|
|
||||||
{ title: "评论数", en: "commentNum" },
|
|
||||||
{ title: "新品", en: "releaseTime" },
|
|
||||||
],
|
|
||||||
goodsList: [], // 商品列表
|
|
||||||
loading: false, // 加载状态
|
|
||||||
total: 0, // 列表总数
|
|
||||||
params: {
|
|
||||||
// 请求参数
|
|
||||||
pageNumber: 1,
|
|
||||||
pageSize: 20,
|
|
||||||
categoryId: "",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
$route() {
|
|
||||||
const { keyword, categoryId, promotionType, promotionsId } = this.$route.query;
|
|
||||||
if (categoryId) {
|
|
||||||
const cateId = categoryId.split(",");
|
|
||||||
Object.assign(this.params, this.$route.query);
|
|
||||||
this.params.categoryId = cateId[cateId.length - 1];
|
|
||||||
}
|
|
||||||
if (promotionType) {
|
|
||||||
this.params.promotionType = promotionType;
|
|
||||||
}
|
|
||||||
if (promotionsId) {
|
|
||||||
this.params.promotionsId = promotionsId;
|
|
||||||
}
|
|
||||||
if (keyword) {
|
|
||||||
// handleSearch 内部会调 getGoodsList,此处不再重复请求
|
|
||||||
this.handleSearch(keyword);
|
|
||||||
} else {
|
|
||||||
this.params.keyword = "";
|
|
||||||
this.getGoodsList();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
displayGoods() {
|
|
||||||
if (this.goodsList.length) return this.goodsList;
|
|
||||||
return this.searched && !this.loading ? this.recommendList : [];
|
|
||||||
},
|
|
||||||
emptyTitle() {
|
|
||||||
const keyword = this.params.keyword;
|
|
||||||
return keyword ? `没有找到与“${keyword}”相关的商品` : "暂无商品";
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// 搜索
|
|
||||||
handleSearch(key) {
|
|
||||||
this.params.keyword = key;
|
|
||||||
this.$route.query.keyword = key
|
|
||||||
this.params.pageNumber = 1;
|
|
||||||
this.getGoodsList();
|
|
||||||
},
|
|
||||||
orderBy(data, index) {
|
|
||||||
// 排序
|
|
||||||
this.sortIndex = index;
|
|
||||||
this.params.sort = data;
|
|
||||||
this.params.order = "desc";
|
|
||||||
if (data === "price") {
|
|
||||||
if (!this.sortPriceIndex) {
|
|
||||||
this.sortPriceIndex = "asc";
|
|
||||||
} else {
|
|
||||||
this.sortPriceIndex === "desc"
|
|
||||||
? (this.sortPriceIndex = "asc")
|
|
||||||
: (this.sortPriceIndex = "desc");
|
|
||||||
}
|
|
||||||
this.params.order = this.sortPriceIndex;
|
|
||||||
} else {
|
|
||||||
this.sortPriceIndex = false;
|
|
||||||
}
|
|
||||||
this.getGoodsList();
|
|
||||||
},
|
|
||||||
formatGrade(grade) {
|
|
||||||
if (grade === null || grade === undefined || grade === "") {
|
|
||||||
return 100;
|
|
||||||
}
|
|
||||||
const num = Number(grade);
|
|
||||||
if (Number.isNaN(num)) {
|
|
||||||
return 100;
|
|
||||||
}
|
|
||||||
const fixed = Number(num.toFixed(1));
|
|
||||||
return fixed % 1 === 0 ? fixed.toFixed(0) : String(fixed);
|
|
||||||
},
|
|
||||||
goGoodsDetail(skuId, goodsId) {
|
|
||||||
const location = {
|
|
||||||
path: "/goodsDetail",
|
|
||||||
query: { skuId, goodsId },
|
|
||||||
};
|
|
||||||
window.open(this.$router.resolve(location).href, "_blank");
|
|
||||||
},
|
|
||||||
goShopPage(id) {
|
|
||||||
if (!id) {
|
|
||||||
Message.warning("店铺信息不存在");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.$router.push({
|
|
||||||
path: "/merchant",
|
|
||||||
query: { id },
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 分页 修改页码
|
|
||||||
changePageNum(val) {
|
|
||||||
this.params.pageNumber = val;
|
|
||||||
this.getGoodsList();
|
|
||||||
},
|
|
||||||
// 分页 修改页数
|
|
||||||
changePageSize(val) {
|
|
||||||
this.params.pageNumber = 1;
|
|
||||||
this.params.pageSize = val;
|
|
||||||
this.getGoodsList();
|
|
||||||
},
|
|
||||||
|
|
||||||
// 获取商品列表
|
|
||||||
getGoodsList() {
|
|
||||||
this.loading = true;
|
|
||||||
apiGoods
|
|
||||||
.goodsList(this.params)
|
|
||||||
.then((res) => {
|
|
||||||
this.loading = false;
|
|
||||||
if (res.success) {
|
|
||||||
this.goodsList = res.result.records || [];
|
|
||||||
this.total = res.result.total;
|
|
||||||
if (!this.goodsList.length) this.loadRecommend();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
this.goodsList = [];
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.loading = false;
|
|
||||||
this.searched = true;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
loadRecommend() {
|
|
||||||
if (this.recommendList.length) return;
|
|
||||||
apiGoods
|
|
||||||
.goodsList({ pageNumber: 1, pageSize: 10, sort: "buyCount", order: "desc" })
|
|
||||||
.then((res) => {
|
|
||||||
if (res.success) this.recommendList = res.result.records || [];
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
// 推荐列表为可选数据,加载失败不阻塞搜索页
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getParams(val) {
|
|
||||||
// 筛选条件回显
|
|
||||||
Object.assign(this.params, val);
|
|
||||||
this.getGoodsList();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
if (this.$route.query.categoryId) {
|
|
||||||
let cateId = this.$route.query.categoryId.split(",");
|
|
||||||
Object.assign(this.params, this.$route.query);
|
|
||||||
this.params.categoryId = cateId[cateId.length - 1];
|
|
||||||
} else {
|
|
||||||
Object.assign(this.params, this.$route.query);
|
|
||||||
}
|
|
||||||
this.getGoodsList();
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
GoodsClassNav,
|
|
||||||
GoodsThumb,
|
|
||||||
ArrowDown,
|
|
||||||
ArrowUp,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, reactive, ref, watch } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
|
import { Message } from "@/utils/message";
|
||||||
|
import { ArrowDown, ArrowUp } from '@element-plus/icons-vue';
|
||||||
|
import GoodsClassNav from "@/components/nav/GoodsClassNav";
|
||||||
|
import GoodsThumb from "@/components/goods/GoodsThumb.vue";
|
||||||
|
import * as apiGoods from "@/api/goods";
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const sortIndex = ref(0); // 排序状态
|
||||||
|
const priceSortIndex = ref(4);
|
||||||
|
const sortPriceIndex = ref(false); // 判断价格升序还是降序
|
||||||
|
const searched = ref(false);
|
||||||
|
const recommendList = ref([]);
|
||||||
|
// 排序类型
|
||||||
|
const goodsTool = [
|
||||||
|
{ title: "综合", en: "" },
|
||||||
|
{ title: "销量", en: "buyCount" },
|
||||||
|
{ title: "评论数", en: "commentNum" },
|
||||||
|
{ title: "新品", en: "releaseTime" },
|
||||||
|
];
|
||||||
|
const goodsList = ref([]); // 商品列表
|
||||||
|
const loading = ref(false); // 加载状态
|
||||||
|
const total = ref(0); // 列表总数
|
||||||
|
// 请求参数
|
||||||
|
const params = reactive({
|
||||||
|
pageNumber: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
categoryId: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const displayGoods = computed(() => {
|
||||||
|
if (goodsList.value.length) return goodsList.value;
|
||||||
|
return searched.value && !loading.value ? recommendList.value : [];
|
||||||
|
});
|
||||||
|
|
||||||
|
const emptyTitle = computed(() => {
|
||||||
|
const keyword = params.keyword;
|
||||||
|
return keyword ? `没有找到与“${keyword}”相关的商品` : "暂无商品";
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => route.query,
|
||||||
|
(query) => {
|
||||||
|
const { keyword, categoryId, promotionType, promotionsId } = query;
|
||||||
|
if (categoryId) {
|
||||||
|
const cateId = categoryId.split(",");
|
||||||
|
Object.assign(params, query);
|
||||||
|
params.categoryId = cateId[cateId.length - 1];
|
||||||
|
}
|
||||||
|
if (promotionType) {
|
||||||
|
params.promotionType = promotionType;
|
||||||
|
}
|
||||||
|
if (promotionsId) {
|
||||||
|
params.promotionsId = promotionsId;
|
||||||
|
}
|
||||||
|
if (keyword) {
|
||||||
|
// handleSearch 内部会调 getGoodsList,此处不再重复请求
|
||||||
|
handleSearch(keyword);
|
||||||
|
} else {
|
||||||
|
params.keyword = "";
|
||||||
|
getGoodsList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
function handleSearch(key) {
|
||||||
|
params.keyword = key;
|
||||||
|
route.query.keyword = key;
|
||||||
|
params.pageNumber = 1;
|
||||||
|
getGoodsList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 排序
|
||||||
|
function orderBy(data, index) {
|
||||||
|
sortIndex.value = index;
|
||||||
|
params.sort = data;
|
||||||
|
params.order = "desc";
|
||||||
|
if (data === "price") {
|
||||||
|
if (!sortPriceIndex.value) {
|
||||||
|
sortPriceIndex.value = "asc";
|
||||||
|
} else {
|
||||||
|
sortPriceIndex.value === "desc"
|
||||||
|
? (sortPriceIndex.value = "asc")
|
||||||
|
: (sortPriceIndex.value = "desc");
|
||||||
|
}
|
||||||
|
params.order = sortPriceIndex.value;
|
||||||
|
} else {
|
||||||
|
sortPriceIndex.value = false;
|
||||||
|
}
|
||||||
|
getGoodsList();
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatGrade(grade) {
|
||||||
|
if (grade === null || grade === undefined || grade === "") {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
const num = Number(grade);
|
||||||
|
if (Number.isNaN(num)) {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
const fixed = Number(num.toFixed(1));
|
||||||
|
return fixed % 1 === 0 ? fixed.toFixed(0) : String(fixed);
|
||||||
|
}
|
||||||
|
|
||||||
|
function goGoodsDetail(skuId, goodsId) {
|
||||||
|
const location = {
|
||||||
|
path: "/goodsDetail",
|
||||||
|
query: { skuId, goodsId },
|
||||||
|
};
|
||||||
|
window.open(router.resolve(location).href, "_blank");
|
||||||
|
}
|
||||||
|
|
||||||
|
function goShopPage(id) {
|
||||||
|
if (!id) {
|
||||||
|
Message.warning("店铺信息不存在");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
router.push({
|
||||||
|
path: "/merchant",
|
||||||
|
query: { id },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页 修改页码
|
||||||
|
function changePageNum(val) {
|
||||||
|
params.pageNumber = val;
|
||||||
|
getGoodsList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页 修改页数
|
||||||
|
function changePageSize(val) {
|
||||||
|
params.pageNumber = 1;
|
||||||
|
params.pageSize = val;
|
||||||
|
getGoodsList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取商品列表
|
||||||
|
function getGoodsList() {
|
||||||
|
loading.value = true;
|
||||||
|
apiGoods
|
||||||
|
.goodsList(params)
|
||||||
|
.then((res) => {
|
||||||
|
loading.value = false;
|
||||||
|
if (res.success) {
|
||||||
|
goodsList.value = res.result.records || [];
|
||||||
|
total.value = res.result.total;
|
||||||
|
if (!goodsList.value.length) loadRecommend();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
goodsList.value = [];
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
searched.value = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadRecommend() {
|
||||||
|
if (recommendList.value.length) return;
|
||||||
|
apiGoods
|
||||||
|
.goodsList({ pageNumber: 1, pageSize: 10, sort: "buyCount", order: "desc" })
|
||||||
|
.then((res) => {
|
||||||
|
if (res.success) recommendList.value = res.result.records || [];
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// 推荐列表为可选数据,加载失败不阻塞搜索页
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 筛选条件回显
|
||||||
|
function getParams(val) {
|
||||||
|
Object.assign(params, val);
|
||||||
|
getGoodsList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化(原 created 钩子逻辑)
|
||||||
|
if (route.query.categoryId) {
|
||||||
|
const cateId = route.query.categoryId.split(",");
|
||||||
|
Object.assign(params, route.query);
|
||||||
|
params.categoryId = cateId[cateId.length - 1];
|
||||||
|
} else {
|
||||||
|
Object.assign(params, route.query);
|
||||||
|
}
|
||||||
|
getGoodsList();
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "../assets/styles/goodsList.scss";
|
@import "../assets/styles/goodsList.scss";
|
||||||
.cate-nav{
|
.cate-nav{
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
<div class="index-main">
|
<div class="index-main">
|
||||||
<!-- 商品分类 + 楼层装修(同一宽度容器,保证与分类栏左缘对齐) -->
|
<!-- 商品分类 + 楼层装修(同一宽度容器,保证与分类栏左缘对齐) -->
|
||||||
<cateNav :showAlways="true" v-if="showNav" :large="carouselLarge" :opacity="carouselOpacity"></cateNav>
|
<cateNav :showAlways="true" v-if="showNav" :large="carouselLarge" :opacity="carouselOpacity"></cateNav>
|
||||||
<model-form ref="modelForm" :data="modelForm"></model-form>
|
<model-form :data="modelFormData"></model-form>
|
||||||
</div>
|
</div>
|
||||||
<!-- 底部栏 -->
|
<!-- 底部栏 -->
|
||||||
<BaseFooter></BaseFooter>
|
<BaseFooter></BaseFooter>
|
||||||
@@ -41,184 +41,179 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { onMounted, onUnmounted, ref } from "vue";
|
||||||
import Search from '@/components/Search';
|
import Search from '@/components/Search';
|
||||||
import ModelForm from '@/components/indexDecorate/ModelForm';
|
import ModelForm from '@/components/indexDecorate/ModelForm';
|
||||||
import HoverSearch from '@/components/header/hoverSearch';
|
import HoverSearch from '@/components/header/hoverSearch';
|
||||||
import fixedBar from '@/components/fixed/index';
|
import fixedBar from '@/components/fixed/index';
|
||||||
import storage from "@/plugins/storage";
|
import storage from "@/plugins/storage";
|
||||||
import { indexData,getAutoCoup } from '@/api/index.js';
|
import { indexData, getAutoCoup } from '@/api/index.js';
|
||||||
import {seckillByDay} from '@/api/promotion'
|
import { seckillByDay } from '@/api/promotion';
|
||||||
export default {
|
|
||||||
name: 'Index',
|
|
||||||
mounted () {
|
|
||||||
this.getIndexData();
|
|
||||||
let that = this;
|
|
||||||
window.onscroll = function () {
|
|
||||||
let top = document.documentElement.scrollTop || document.body.scrollTop;
|
|
||||||
if (top > 300) {
|
|
||||||
that.topSearchShow = true;
|
|
||||||
} else {
|
|
||||||
that.topSearchShow = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if(storage.getItem('userInfo')){
|
|
||||||
this.getAutoCoup()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
autoCoupList:[],
|
|
||||||
showCpmodel:false,
|
|
||||||
modelForm: { list: [] }, // 楼层装修数据
|
|
||||||
topAdvert: {}, // 顶部广告
|
|
||||||
showNav: false, // 是否展示分类栏
|
|
||||||
topSearchShow: false, // 滚动后顶部搜索栏展示
|
|
||||||
carouselLarge: false, // 不同轮播分类尺寸
|
|
||||||
carouselOpacity: false // 不同轮播分类样式
|
|
||||||
};
|
|
||||||
},
|
|
||||||
props:{
|
|
||||||
pageData:{
|
|
||||||
type:null,
|
|
||||||
default:""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// created(){
|
|
||||||
|
|
||||||
// },
|
defineOptions({ name: "Index" });
|
||||||
methods: {
|
|
||||||
// 优惠券可用范围
|
const props = defineProps({
|
||||||
useScope (type, storeName) {
|
pageData: {
|
||||||
let shop = '平台';
|
type: null,
|
||||||
let goods = '全部商品'
|
default: "",
|
||||||
if (storeName !== 'platform') shop = storeName
|
},
|
||||||
switch (type) {
|
});
|
||||||
case 'ALL':
|
|
||||||
goods = '全部商品'
|
const autoCoupList = ref([]);
|
||||||
break;
|
const showCpmodel = ref(false);
|
||||||
case 'PORTION_GOODS':
|
const modelFormData = ref({ list: [] }); // 楼层装修数据
|
||||||
goods = '部分商品'
|
const topAdvert = ref({}); // 顶部广告
|
||||||
break;
|
const showNav = ref(false); // 是否展示分类栏
|
||||||
case 'PORTION_GOODS_CATEGORY':
|
const topSearchShow = ref(false); // 滚动后顶部搜索栏展示
|
||||||
goods = '部分分类商品'
|
const carouselLarge = ref(false); // 不同轮播分类尺寸
|
||||||
break;
|
const carouselOpacity = ref(false); // 不同轮播分类样式
|
||||||
|
|
||||||
|
// 优惠券可用范围
|
||||||
|
function useScope(type, storeName) {
|
||||||
|
let shop = '平台';
|
||||||
|
let goods = '全部商品';
|
||||||
|
if (storeName !== 'platform') shop = storeName;
|
||||||
|
switch (type) {
|
||||||
|
case 'ALL':
|
||||||
|
goods = '全部商品';
|
||||||
|
break;
|
||||||
|
case 'PORTION_GOODS':
|
||||||
|
goods = '部分商品';
|
||||||
|
break;
|
||||||
|
case 'PORTION_GOODS_CATEGORY':
|
||||||
|
goods = '部分分类商品';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return `${shop}${goods}可用`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchAutoCoup() {
|
||||||
|
let data = new Date();
|
||||||
|
let datas = data.getDate();
|
||||||
|
let hours = data.getHours();
|
||||||
|
let flagCoup = storage.getItem('getTimes'); //缓存
|
||||||
|
if (flagCoup && flagCoup != undefined && flagCoup != null) { //判断当前是否有缓存
|
||||||
|
if (Number(datas) > Number(flagCoup)) { //判断缓存是否小于当前天数
|
||||||
|
if (Number(hours) >= 6) { //超过或等于6时清楚缓存
|
||||||
|
storage.setItem('getTimes', datas); //存储缓存
|
||||||
|
getcps();
|
||||||
}
|
}
|
||||||
return `${shop}${goods}可用`
|
}
|
||||||
},
|
} else {
|
||||||
getAutoCoup(){
|
getcps();
|
||||||
let data = new Date()
|
}
|
||||||
let datas = data.getDate()
|
}
|
||||||
let hours = data.getHours()
|
|
||||||
let flagCoup = storage.getItem('getTimes') //缓存
|
function getcps() {
|
||||||
if(flagCoup && flagCoup != undefined && flagCoup != null){ //判断当前是否有缓存
|
let data = new Date();
|
||||||
if(Number(datas) > Number(flagCoup)){ //判断缓存是否小于当前天数
|
let datas = data.getDate();
|
||||||
if(Number(hours) >= 6){//超过或等于6时清楚缓存
|
getAutoCoup().then(res => { //调用自动发券
|
||||||
storage.setItem('getTimes',datas)//存储缓存
|
if (res.success) {
|
||||||
this.getcps()
|
autoCoupList.value.push(...res.result);
|
||||||
}
|
let objs = {};
|
||||||
|
autoCoupList.value = autoCoupList.value.reduce((cur, next) => {
|
||||||
|
//对象去重
|
||||||
|
if (next.id != undefined) {
|
||||||
|
objs[next.id]
|
||||||
|
? ""
|
||||||
|
: (objs[next.id] = true && cur.push(next));
|
||||||
}
|
}
|
||||||
}else{
|
return cur;
|
||||||
// window.localStorage.setItem('getTimes',datas)//存储缓存
|
}, []);
|
||||||
this.getcps()
|
if (autoCoupList.value != '' && autoCoupList.value.length > 0) {
|
||||||
|
showCpmodel.value = true;
|
||||||
}
|
}
|
||||||
},
|
storage.setItem('getTimes', datas); //存储缓存
|
||||||
getcps(){
|
}
|
||||||
let data = new Date()
|
}).catch((err) => {
|
||||||
let datas = data.getDate()
|
console.warn('获取自动发券信息失败', err);
|
||||||
getAutoCoup().then(res=>{ //调用自动发券
|
});
|
||||||
if(res.success){
|
}
|
||||||
this.autoCoupList.push(...res.result);
|
|
||||||
let objs = {};
|
|
||||||
this.autoCoupList = this.autoCoupList.reduce((cur, next) => {
|
|
||||||
//对象去重
|
|
||||||
if (next.id != undefined) {
|
|
||||||
objs[next.id]
|
|
||||||
? ""
|
|
||||||
: (objs[next.id] = true && cur.push(next));
|
|
||||||
}
|
|
||||||
return cur;
|
|
||||||
}, []);
|
|
||||||
if(this.autoCoupList != '' && this.autoCoupList.length > 0){
|
|
||||||
this.showCpmodel = true;
|
|
||||||
}
|
|
||||||
storage.setItem('getTimes',datas)//存储缓存
|
|
||||||
}
|
|
||||||
}).catch((err) => {
|
|
||||||
console.warn('获取自动发券信息失败', err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
handleReachBottom(){
|
|
||||||
|
|
||||||
} ,
|
function handleReachBottom() {
|
||||||
getIndexData () {
|
}
|
||||||
if(this.pageData){
|
|
||||||
this.parsePageData(JSON.stringify(this.pageData)).catch(() => {
|
function getIndexData() {
|
||||||
|
if (props.pageData) {
|
||||||
|
parsePageData(JSON.stringify(props.pageData)).catch(() => {
|
||||||
|
// 装修数据解析失败时保持默认空楼层
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 获取首页装修数据
|
||||||
|
indexData({ clientType: 'PC' }).then(async (res) => {
|
||||||
|
if (res.success && res.result?.pageData) {
|
||||||
|
try {
|
||||||
|
await parsePageData(res.result.pageData);
|
||||||
|
} catch {
|
||||||
// 装修数据解析失败时保持默认空楼层
|
// 装修数据解析失败时保持默认空楼层
|
||||||
});
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
// 获取首页装修数据
|
|
||||||
indexData({ clientType: 'PC' }).then(async (res) => {
|
|
||||||
if (res.success && res.result?.pageData) {
|
|
||||||
try {
|
|
||||||
await this.parsePageData(res.result.pageData);
|
|
||||||
} catch {
|
|
||||||
// 装修数据解析失败时保持默认空楼层
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).catch((err) => {
|
|
||||||
console.warn('获取首页装修数据失败', err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async parsePageData(pageData){
|
|
||||||
const dataJson = typeof pageData === "string" ? JSON.parse(pageData) : pageData;
|
|
||||||
if (!dataJson?.list?.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 秒杀活动不是装修的数据,需要调用接口判断是否有秒杀商品
|
|
||||||
// 轮播图根据不同轮播,样式不同
|
|
||||||
for (let i = 0; i < dataJson.list.length; i++) {
|
|
||||||
let type = dataJson.list[i].type
|
|
||||||
if (type === 'carousel2') {
|
|
||||||
this.carouselLarge = true;
|
|
||||||
} else if (type === 'carousel1') {
|
|
||||||
this.carouselLarge = true
|
|
||||||
this.carouselOpacity = true
|
|
||||||
} else if (type === 'seckill') {
|
|
||||||
let seckill = await this.getListByDay()
|
|
||||||
dataJson.list[i].options.list = seckill
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.modelForm = dataJson;
|
}).catch((err) => {
|
||||||
if (dataJson.list[1]) {
|
console.warn('获取首页装修数据失败', err);
|
||||||
storage.setItem('navList', dataJson.list[1])
|
});
|
||||||
}
|
}
|
||||||
this.showNav = true
|
}
|
||||||
if (dataJson.list[0]) {
|
|
||||||
this.topAdvert = dataJson.list[0];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async getListByDay () { // 当天秒杀活动
|
async function parsePageData(pageData) {
|
||||||
try {
|
const dataJson = typeof pageData === "string" ? JSON.parse(pageData) : pageData;
|
||||||
const res = await seckillByDay()
|
if (!dataJson?.list?.length) {
|
||||||
if (res.success && res.result?.length) {
|
return;
|
||||||
return res.result
|
}
|
||||||
}
|
// 秒杀活动不是装修的数据,需要调用接口判断是否有秒杀商品
|
||||||
} catch {
|
// 轮播图根据不同轮播,样式不同
|
||||||
// 秒杀接口失败时不影响首页其他模块
|
for (let i = 0; i < dataJson.list.length; i++) {
|
||||||
}
|
let type = dataJson.list[i].type;
|
||||||
return []
|
if (type === 'carousel2') {
|
||||||
|
carouselLarge.value = true;
|
||||||
|
} else if (type === 'carousel1') {
|
||||||
|
carouselLarge.value = true;
|
||||||
|
carouselOpacity.value = true;
|
||||||
|
} else if (type === 'seckill') {
|
||||||
|
let seckill = await getListByDay();
|
||||||
|
dataJson.list[i].options.list = seckill;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
components: { Search,
|
modelFormData.value = dataJson;
|
||||||
ModelForm,
|
if (dataJson.list[1]) {
|
||||||
HoverSearch,
|
storage.setItem('navList', dataJson.list[1]);
|
||||||
fixedBar }
|
}
|
||||||
};
|
showNav.value = true;
|
||||||
|
if (dataJson.list[0]) {
|
||||||
|
topAdvert.value = dataJson.list[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getListByDay() { // 当天秒杀活动
|
||||||
|
try {
|
||||||
|
const res = await seckillByDay();
|
||||||
|
if (res.success && res.result?.length) {
|
||||||
|
return res.result;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// 秒杀接口失败时不影响首页其他模块
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleScroll() {
|
||||||
|
const top = document.documentElement.scrollTop || document.body.scrollTop;
|
||||||
|
topSearchShow.value = top > 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getIndexData();
|
||||||
|
window.onscroll = handleScroll;
|
||||||
|
if (storage.getItem('userInfo')) {
|
||||||
|
fetchAutoCoup();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (window.onscroll === handleScroll) {
|
||||||
|
window.onscroll = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<!-- 顶部logo -->
|
<!-- 顶部logo -->
|
||||||
<div class="top-content" @click="hideVerify">
|
<div class="top-content" @click="hideVerify">
|
||||||
<div class="logo-box">
|
<div class="logo-box">
|
||||||
<img :src="$store.state.logoImg" @click="$router.push('/')" />
|
<img :src="logoImg" @click="$router.push('/')" />
|
||||||
<div>欢迎登录</div>
|
<div>欢迎登录</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
<!-- 账号密码登录 -->
|
<!-- 账号密码登录 -->
|
||||||
<el-form ref="formInline" :model="formData" :rules="ruleInline" v-show="type === true"
|
<el-form ref="formInlineRef" :model="formData" :rules="ruleInline" v-show="type === true"
|
||||||
@click.self="hideVerify">
|
@click.self="hideVerify">
|
||||||
<el-form-item prop="username">
|
<el-form-item prop="username">
|
||||||
<el-input type="text" v-model="formData.username" clearable placeholder="用户名">
|
<el-input type="text" v-model="formData.username" clearable placeholder="用户名">
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<!-- 验证码登录 -->
|
<!-- 验证码登录 -->
|
||||||
<el-form ref="formSms" :model="formSms" :rules="ruleInline" v-show="type === false"
|
<el-form ref="formSmsRef" :model="formSms" :rules="ruleInline" v-show="type === false"
|
||||||
@click.self="hideVerify">
|
@click.self="hideVerify">
|
||||||
<el-form-item prop="mobile">
|
<el-form-item prop="mobile">
|
||||||
<el-input type="text" v-model="formSms.mobile" clearable placeholder="手机号">
|
<el-input type="text" v-model="formSms.mobile" clearable placeholder="手机号">
|
||||||
@@ -88,7 +88,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<!-- 拼图验证码 -->
|
<!-- 拼图验证码 -->
|
||||||
<verify ref="verify" class="verify-con" verifyType="LOGIN" @change="verifyChange"></verify>
|
<verify ref="verifyRef" class="verify-con" verifyType="LOGIN" @change="verifyChange"></verify>
|
||||||
</div>
|
</div>
|
||||||
<div class="foot">
|
<div class="foot">
|
||||||
<div type="flex" justify="space-around" class="help">
|
<div type="flex" justify="space-around" class="help">
|
||||||
@@ -105,8 +105,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
|
import { computed, onBeforeUnmount, onMounted, reactive, ref, watch } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { Message, Spin } from "@/utils/message";
|
import { Message, Spin } from "@/utils/message";
|
||||||
import * as RegExp from "@/plugins/RegExp.js";
|
import * as RegExp from "@/plugins/RegExp.js";
|
||||||
import { md5 } from "@/plugins/md5.js";
|
import { md5 } from "@/plugins/md5.js";
|
||||||
@@ -117,235 +119,240 @@ import storage from "@/plugins/storage.js";
|
|||||||
import verify from "@/components/verify";
|
import verify from "@/components/verify";
|
||||||
import { User, Lock, Document, Iphone } from "@element-plus/icons-vue";
|
import { User, Lock, Document, Iphone } from "@element-plus/icons-vue";
|
||||||
import config from "@/config";
|
import config from "@/config";
|
||||||
|
import { useAppStore } from "@/stores/app";
|
||||||
|
|
||||||
export default {
|
defineOptions({ name: "Login" });
|
||||||
name: "Login",
|
|
||||||
components: {
|
|
||||||
verify,
|
|
||||||
User,
|
|
||||||
Lock,
|
|
||||||
Document,
|
|
||||||
Iphone,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
config,
|
|
||||||
type: true, // true 账号登录 false 验证码登录
|
|
||||||
formData: {
|
|
||||||
// 登录表单
|
|
||||||
username: "",
|
|
||||||
password: "",
|
|
||||||
},
|
|
||||||
formSms: {
|
|
||||||
// 手机号登录
|
|
||||||
code: "",
|
|
||||||
mobile: "",
|
|
||||||
},
|
|
||||||
verifyStatus: false, // 是否图片验证通过
|
|
||||||
codeSent: false, // 是否已成功发送短信验证码
|
|
||||||
ruleInline: {
|
|
||||||
// 验证规则
|
|
||||||
username: [{required: true, message: "请输入用户名"}],
|
|
||||||
password: [
|
|
||||||
{required: true, message: "请输入密码"},
|
|
||||||
{type: "string", min: 6, message: "密码不能少于6位"},
|
|
||||||
],
|
|
||||||
mobile: [
|
|
||||||
{required: true, message: "请输入手机号码"},
|
|
||||||
{
|
|
||||||
pattern: RegExp.mobile,
|
|
||||||
message: "请输入正确的手机号",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
code: [{required: true, message: "请输入手机验证码"}],
|
|
||||||
},
|
|
||||||
codeMsg: "发送验证码", // 验证码文字
|
|
||||||
interval: null, // 定时器
|
|
||||||
time: 60, // 倒计时
|
|
||||||
year: new Date().getFullYear(),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
hideVerify() {
|
|
||||||
this.$refs.verify?.close?.();
|
|
||||||
},
|
|
||||||
|
|
||||||
// 登录
|
const route = useRoute();
|
||||||
handleSubmit(name) {
|
const router = useRouter();
|
||||||
this.$refs[name].validate((valid) => {
|
const appStore = useAppStore();
|
||||||
if (valid) {
|
|
||||||
if (this.type) {
|
|
||||||
this.$refs.verify?.init();
|
|
||||||
} else {
|
|
||||||
if (!this.codeSent) {
|
|
||||||
Message.warning("请先获取短信验证码");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const data = JSON.parse(JSON.stringify(this.formSms));
|
|
||||||
apiLogin.smsLogin(data).then((res) => {
|
|
||||||
this.hideVerify();
|
|
||||||
if (res.success) {
|
|
||||||
Message.success("登录成功");
|
|
||||||
storage.setItem("accessToken", res.result.accessToken);
|
|
||||||
storage.setItem("refreshToken", res.result.refreshToken);
|
|
||||||
storage.setItem('getTimes',0)
|
|
||||||
apiLogin.getMemberMsg().then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
storage.setItem("userInfo", res.result);
|
|
||||||
let query = this.$route.query;
|
|
||||||
if (query.rePath) {
|
|
||||||
this.$router.push({
|
|
||||||
path: query.rePath,
|
|
||||||
query: JSON.parse(query.query),
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.$router.push("/");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
Message.error(res.message);
|
|
||||||
}
|
|
||||||
}).catch((err) => {
|
|
||||||
console.warn('短信登录失败', err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 发送手机验证码
|
|
||||||
sendCode() {
|
|
||||||
if (this.time !== 60) return;
|
|
||||||
if (this.formSms.mobile === "") {
|
|
||||||
Message.warning("请先填写手机号");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!RegExp.mobile.test(this.formSms.mobile)) {
|
|
||||||
Message.warning("请输入正确的手机号");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.$refs.verify?.init();
|
|
||||||
},
|
|
||||||
dispatchSms() {
|
|
||||||
sendSms({
|
|
||||||
mobile: this.formSms.mobile,
|
|
||||||
verificationEnums: "LOGIN",
|
|
||||||
}).then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
Message.success("验证码发送成功");
|
|
||||||
this.codeSent = true;
|
|
||||||
this.interval = setInterval(() => {
|
|
||||||
this.time--;
|
|
||||||
if (this.time === 0) {
|
|
||||||
this.time = 60;
|
|
||||||
this.codeMsg = "重新发送";
|
|
||||||
this.verifyStatus = false;
|
|
||||||
this.codeSent = false;
|
|
||||||
clearInterval(this.interval);
|
|
||||||
} else {
|
|
||||||
this.codeMsg = this.time;
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
} else {
|
|
||||||
this.verifyStatus = false;
|
|
||||||
Message.warning(res.message);
|
|
||||||
}
|
|
||||||
}).catch(() => {
|
|
||||||
this.verifyStatus = false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
verifyChange(con) {
|
|
||||||
// 拼图验证码回显
|
|
||||||
if (!con.status) return;
|
|
||||||
|
|
||||||
if (this.type === true) {
|
const logoImg = computed(() => appStore.logoImg);
|
||||||
// 账号密码登录
|
|
||||||
let data = JSON.parse(JSON.stringify(this.formData));
|
|
||||||
data.password = md5(data.password);
|
|
||||||
this.hideVerify();
|
|
||||||
Spin.show();
|
|
||||||
apiLogin
|
|
||||||
.login(data)
|
|
||||||
.then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
this.loginSuccess(res.result.accessToken,res.result.refreshToken);
|
|
||||||
} else {
|
|
||||||
Spin.hide();
|
|
||||||
Message.error(res.message);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
Spin.hide();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.verifyStatus = true;
|
|
||||||
this.hideVerify();
|
|
||||||
this.dispatchSms();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleWebLogin(type) {
|
|
||||||
// 第三方登录
|
|
||||||
webLogin(type);
|
|
||||||
},
|
|
||||||
loginSuccess(accessToken,refreshToken){
|
|
||||||
Message.success("登录成功");
|
|
||||||
storage.setItem("accessToken", accessToken);
|
|
||||||
storage.setItem("refreshToken", refreshToken);
|
|
||||||
apiLogin.getMemberMsg().then((res) => {
|
|
||||||
Spin.hide();
|
|
||||||
if (res.success) {
|
|
||||||
storage.setItem("userInfo", res.result);
|
|
||||||
let query = this.$route.query;
|
|
||||||
if (query.rePath) {
|
|
||||||
this.$router.push({
|
|
||||||
path: query.rePath,
|
|
||||||
query: JSON.parse(query.query),
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.$router.push("/");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
},
|
const formInlineRef = ref(null);
|
||||||
beforeUnmount() {
|
const formSmsRef = ref(null);
|
||||||
clearInterval(this.interval);
|
const verifyRef = ref(null);
|
||||||
},
|
const formRefs = {
|
||||||
mounted() {
|
formInline: formInlineRef,
|
||||||
let uuid = this.$route.query.state;
|
formSms: formSmsRef,
|
||||||
if (uuid) {
|
|
||||||
storage.setItem("uuid", uuid);
|
|
||||||
loginCallback(uuid).then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
this.loginSuccess(res.result.accessToken,res.result.refreshToken);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
type(v) {
|
|
||||||
if (v) {
|
|
||||||
this.$refs.formInline.resetFields();
|
|
||||||
} else {
|
|
||||||
this.$refs.formSms.resetFields();
|
|
||||||
}
|
|
||||||
this.verifyStatus = false;
|
|
||||||
this.codeSent = false;
|
|
||||||
this.hideVerify();
|
|
||||||
clearInterval(this.interval);
|
|
||||||
this.codeMsg = "发送验证码";
|
|
||||||
this.time = 60;
|
|
||||||
},
|
|
||||||
"formSms.mobile"() {
|
|
||||||
this.codeSent = false;
|
|
||||||
this.verifyStatus = false;
|
|
||||||
clearInterval(this.interval);
|
|
||||||
this.codeMsg = "发送验证码";
|
|
||||||
this.time = 60;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const type = ref(true); // true 账号登录 false 验证码登录
|
||||||
|
const formData = reactive({
|
||||||
|
// 登录表单
|
||||||
|
username: "",
|
||||||
|
password: "",
|
||||||
|
});
|
||||||
|
const formSms = reactive({
|
||||||
|
// 手机号登录
|
||||||
|
code: "",
|
||||||
|
mobile: "",
|
||||||
|
});
|
||||||
|
const verifyStatus = ref(false); // 是否图片验证通过
|
||||||
|
const codeSent = ref(false); // 是否已成功发送短信验证码
|
||||||
|
const ruleInline = {
|
||||||
|
// 验证规则
|
||||||
|
username: [{required: true, message: "请输入用户名"}],
|
||||||
|
password: [
|
||||||
|
{required: true, message: "请输入密码"},
|
||||||
|
{type: "string", min: 6, message: "密码不能少于6位"},
|
||||||
|
],
|
||||||
|
mobile: [
|
||||||
|
{required: true, message: "请输入手机号码"},
|
||||||
|
{
|
||||||
|
pattern: RegExp.mobile,
|
||||||
|
message: "请输入正确的手机号",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
code: [{required: true, message: "请输入手机验证码"}],
|
||||||
|
};
|
||||||
|
const codeMsg = ref("发送验证码"); // 验证码文字
|
||||||
|
const interval = ref(null); // 定时器
|
||||||
|
const time = ref(60); // 倒计时
|
||||||
|
const year = new Date().getFullYear();
|
||||||
|
|
||||||
|
function hideVerify() {
|
||||||
|
verifyRef.value?.close?.();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 登录
|
||||||
|
function handleSubmit(name) {
|
||||||
|
formRefs[name].value.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
if (type.value) {
|
||||||
|
verifyRef.value?.init();
|
||||||
|
} else {
|
||||||
|
if (!codeSent.value) {
|
||||||
|
Message.warning("请先获取短信验证码");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data = JSON.parse(JSON.stringify(formSms));
|
||||||
|
apiLogin.smsLogin(data).then((res) => {
|
||||||
|
hideVerify();
|
||||||
|
if (res.success) {
|
||||||
|
Message.success("登录成功");
|
||||||
|
storage.setItem("accessToken", res.result.accessToken);
|
||||||
|
storage.setItem("refreshToken", res.result.refreshToken);
|
||||||
|
storage.setItem('getTimes',0)
|
||||||
|
apiLogin.getMemberMsg().then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
storage.setItem("userInfo", res.result);
|
||||||
|
let query = route.query;
|
||||||
|
if (query.rePath) {
|
||||||
|
router.push({
|
||||||
|
path: query.rePath,
|
||||||
|
query: JSON.parse(query.query),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
router.push("/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Message.error(res.message);
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
console.warn('短信登录失败', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 发送手机验证码
|
||||||
|
function sendCode() {
|
||||||
|
if (time.value !== 60) return;
|
||||||
|
if (formSms.mobile === "") {
|
||||||
|
Message.warning("请先填写手机号");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!RegExp.mobile.test(formSms.mobile)) {
|
||||||
|
Message.warning("请输入正确的手机号");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
verifyRef.value?.init();
|
||||||
|
}
|
||||||
|
function dispatchSms() {
|
||||||
|
sendSms({
|
||||||
|
mobile: formSms.mobile,
|
||||||
|
verificationEnums: "LOGIN",
|
||||||
|
}).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
Message.success("验证码发送成功");
|
||||||
|
codeSent.value = true;
|
||||||
|
interval.value = setInterval(() => {
|
||||||
|
time.value--;
|
||||||
|
if (time.value === 0) {
|
||||||
|
time.value = 60;
|
||||||
|
codeMsg.value = "重新发送";
|
||||||
|
verifyStatus.value = false;
|
||||||
|
codeSent.value = false;
|
||||||
|
clearInterval(interval.value);
|
||||||
|
} else {
|
||||||
|
codeMsg.value = time.value;
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
} else {
|
||||||
|
verifyStatus.value = false;
|
||||||
|
Message.warning(res.message);
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
verifyStatus.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function verifyChange(con) {
|
||||||
|
// 拼图验证码回显
|
||||||
|
if (!con.status) return;
|
||||||
|
|
||||||
|
if (type.value === true) {
|
||||||
|
// 账号密码登录
|
||||||
|
let data = JSON.parse(JSON.stringify(formData));
|
||||||
|
data.password = md5(data.password);
|
||||||
|
hideVerify();
|
||||||
|
Spin.show();
|
||||||
|
apiLogin
|
||||||
|
.login(data)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
loginSuccess(res.result.accessToken,res.result.refreshToken);
|
||||||
|
} else {
|
||||||
|
Spin.hide();
|
||||||
|
Message.error(res.message);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
Spin.hide();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
verifyStatus.value = true;
|
||||||
|
hideVerify();
|
||||||
|
dispatchSms();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function handleWebLogin(type) {
|
||||||
|
// 第三方登录
|
||||||
|
webLogin(type);
|
||||||
|
}
|
||||||
|
function loginSuccess(accessToken,refreshToken){
|
||||||
|
Message.success("登录成功");
|
||||||
|
storage.setItem("accessToken", accessToken);
|
||||||
|
storage.setItem("refreshToken", refreshToken);
|
||||||
|
apiLogin.getMemberMsg().then((res) => {
|
||||||
|
Spin.hide();
|
||||||
|
if (res.success) {
|
||||||
|
storage.setItem("userInfo", res.result);
|
||||||
|
let query = route.query;
|
||||||
|
if (query.rePath) {
|
||||||
|
router.push({
|
||||||
|
path: query.rePath,
|
||||||
|
query: JSON.parse(query.query),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
router.push("/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
clearInterval(interval.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
let uuid = route.query.state;
|
||||||
|
if (uuid) {
|
||||||
|
storage.setItem("uuid", uuid);
|
||||||
|
loginCallback(uuid).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
loginSuccess(res.result.accessToken,res.result.refreshToken);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(type, (v) => {
|
||||||
|
if (v) {
|
||||||
|
formInlineRef.value.resetFields();
|
||||||
|
} else {
|
||||||
|
formSmsRef.value.resetFields();
|
||||||
|
}
|
||||||
|
verifyStatus.value = false;
|
||||||
|
codeSent.value = false;
|
||||||
|
hideVerify();
|
||||||
|
clearInterval(interval.value);
|
||||||
|
codeMsg.value = "发送验证码";
|
||||||
|
time.value = 60;
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => formSms.mobile,
|
||||||
|
() => {
|
||||||
|
codeSent.value = false;
|
||||||
|
verifyStatus.value = false;
|
||||||
|
clearInterval(interval.value);
|
||||||
|
codeMsg.value = "发送验证码";
|
||||||
|
time.value = 60;
|
||||||
|
}
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.login {
|
.login {
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
|
|
||||||
<div v-if="showFloorView">
|
<div v-if="showFloorView">
|
||||||
<!-- 楼层装修部分 -->
|
<!-- 楼层装修部分 -->
|
||||||
<model-form ref="modelForm" :data="modelForm"></model-form>
|
<model-form ref="modelFormRef" :data="modelForm"></model-form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@@ -120,9 +120,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
import { Message } from "@/utils/message";
|
import { reactive, ref } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
|
import { Message, Modal } from "@/utils/message";
|
||||||
import { ArrowDown, StarFilled } from '@element-plus/icons-vue';
|
import { ArrowDown, StarFilled } from '@element-plus/icons-vue';
|
||||||
import {getCateById, getDetailById} from "@/api/shopentry";
|
import {getCateById, getDetailById} from "@/api/shopentry";
|
||||||
import {
|
import {
|
||||||
@@ -130,254 +132,325 @@ import {
|
|||||||
collectStore,
|
collectStore,
|
||||||
isStoreCollection,
|
isStoreCollection,
|
||||||
} from "@/api/member";
|
} from "@/api/member";
|
||||||
import {goodsList} from "@/api/goods";
|
import {goodsList as goodsListApi} from "@/api/goods";
|
||||||
import Search from "@/components/Search";
|
import Search from "@/components/Search";
|
||||||
import ModelForm from "@/components/indexDecorate/ModelForm";
|
import ModelForm from "@/components/indexDecorate/ModelForm";
|
||||||
import storage from "@/plugins/storage";
|
import storage from "@/plugins/storage";
|
||||||
import {getFloorStoreData} from "@/api/index.js";
|
import {getFloorStoreData} from "@/api/index.js";
|
||||||
import { seckillByDay } from "@/api/promotion";
|
import { seckillByDay } from "@/api/promotion";
|
||||||
import imTalk from '@/components/mixes/talkIm'
|
import { getIMDetail } from "@/api/common";
|
||||||
|
import { getMemberMsg } from "@/api/login";
|
||||||
|
|
||||||
export default {
|
defineOptions({ name: "Merchant" });
|
||||||
name: "Merchant",
|
|
||||||
components: { Search, ModelForm, StarFilled, ArrowDown },
|
const route = useRoute();
|
||||||
mixins: [imTalk],
|
const router = useRouter();
|
||||||
data() {
|
|
||||||
return {
|
// 店铺装修的内容
|
||||||
// 店铺装修的内容
|
const modelForm = ref({list: []}); // 楼层装修数据
|
||||||
modelForm: {list: []}, // 楼层装修数据
|
const modelFormRef = ref(null);
|
||||||
topAdvert: {}, // 顶部广告
|
const topAdvert = ref({}); // 顶部广告
|
||||||
showNav: false, // 是否展示分类栏
|
const showNav = ref(false); // 是否展示分类栏
|
||||||
topSearchShow: false, // 滚动后顶部搜索栏展示
|
const topSearchShow = ref(false); // 滚动后顶部搜索栏展示
|
||||||
carouselLarge: false, // 不同轮播分类尺寸
|
const carouselLarge = ref(false); // 不同轮播分类尺寸
|
||||||
carouselOpacity: false, // 不同轮播分类样式,
|
const carouselOpacity = ref(false); // 不同轮播分类样式,
|
||||||
enablePageData: false, //是否显示楼层装修内容
|
const enablePageData = ref(false); //是否显示楼层装修内容
|
||||||
basePageData: false, //基础店铺信息
|
const basePageData = ref(false); //基础店铺信息
|
||||||
showFloorView: false, // 是否展示楼层装修(有有效装修数据时为 true)
|
const showFloorView = ref(false); // 是否展示楼层装修(有有效装修数据时为 true)
|
||||||
storeMsg: {}, // 店铺信息
|
const storeMsg = ref({}); // 店铺信息
|
||||||
cateList: [], // 店铺分裂
|
const cateList = ref([]); // 店铺分裂
|
||||||
goodsList: [], // 商品列表
|
const goodsList = ref([]); // 商品列表
|
||||||
total: 0, // 商品数量
|
const total = ref(0); // 商品数量
|
||||||
params: {
|
const params = reactive({
|
||||||
// 请求参数
|
// 请求参数
|
||||||
pageNumber: 1,
|
pageNumber: 1,
|
||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
keyword: "",
|
keyword: "",
|
||||||
storeId: this.$route.query.id,
|
storeId: route.query.id,
|
||||||
storeCatId: "",
|
storeCatId: "",
|
||||||
},
|
});
|
||||||
cateName: "店铺推荐", // 分类名称
|
const cateName = ref("店铺推荐"); // 分类名称
|
||||||
storeCollected: false, // 是否收藏
|
const storeCollected = ref(false); // 是否收藏
|
||||||
};
|
// 原 talkIm mixin 的 IM 链接
|
||||||
},
|
const IMLink = ref("");
|
||||||
created() {
|
|
||||||
this.getStoreMsg();
|
function isFloorEnabled() {
|
||||||
window.onscroll = () => {
|
const pageShow = storeMsg.value && storeMsg.value.pageShow;
|
||||||
const top =
|
return pageShow === "1" || pageShow === 1 || pageShow === "OPEN" || pageShow === true;
|
||||||
document.documentElement.scrollTop || document.body.scrollTop;
|
}
|
||||||
this.topSearchShow = top > 300;
|
|
||||||
};
|
function loadStoreGoodsView() {
|
||||||
},
|
showFloorView.value = false;
|
||||||
methods: {
|
getGoodsList();
|
||||||
isFloorEnabled() {
|
getCateList();
|
||||||
const pageShow = this.storeMsg && this.storeMsg.pageShow;
|
}
|
||||||
return pageShow === "1" || pageShow === 1 || pageShow === "OPEN" || pageShow === true;
|
|
||||||
},
|
function getIndexData() {
|
||||||
loadStoreGoodsView() {
|
getFloorStoreData({
|
||||||
this.showFloorView = false;
|
clientType: "PC",
|
||||||
this.getGoodsList();
|
num: route.query.id,
|
||||||
this.getCateList();
|
pageType: "STORE",
|
||||||
},
|
})
|
||||||
getIndexData() {
|
.then(async (res) => {
|
||||||
getFloorStoreData({
|
if (res.success && res.result?.pageData) {
|
||||||
clientType: "PC",
|
try {
|
||||||
num: this.$route.query.id,
|
await parsePageData(res.result.pageData);
|
||||||
pageType: "STORE",
|
showFloorView.value = true;
|
||||||
})
|
} catch {
|
||||||
.then(async (res) => {
|
loadStoreGoodsView();
|
||||||
if (res.success && res.result?.pageData) {
|
|
||||||
try {
|
|
||||||
await this.parsePageData(res.result.pageData);
|
|
||||||
this.showFloorView = true;
|
|
||||||
} catch {
|
|
||||||
this.loadStoreGoodsView();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.loadStoreGoodsView();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
this.loadStoreGoodsView();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
async parsePageData(pageData) {
|
|
||||||
const dataJson = JSON.parse(pageData);
|
|
||||||
for (let i = 0; i < dataJson.list.length; i++) {
|
|
||||||
const type = dataJson.list[i].type;
|
|
||||||
if (type === "carousel2") {
|
|
||||||
this.carouselLarge = true;
|
|
||||||
} else if (type === "carousel1") {
|
|
||||||
this.carouselLarge = true;
|
|
||||||
this.carouselOpacity = true;
|
|
||||||
} else if (type === "seckill") {
|
|
||||||
dataJson.list[i].options.list = await this.getListByDay();
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
loadStoreGoodsView();
|
||||||
}
|
}
|
||||||
this.modelForm = dataJson;
|
})
|
||||||
storage.setItem("navList", dataJson.list[1]);
|
.catch(() => {
|
||||||
this.showNav = true;
|
loadStoreGoodsView();
|
||||||
this.topAdvert = dataJson.list[0];
|
});
|
||||||
},
|
}
|
||||||
async getListByDay() {
|
|
||||||
const res = await seckillByDay();
|
|
||||||
if (res.success && res.result?.length) {
|
|
||||||
return res.result;
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
|
|
||||||
|
async function parsePageData(pageData) {
|
||||||
|
const dataJson = JSON.parse(pageData);
|
||||||
|
for (let i = 0; i < dataJson.list.length; i++) {
|
||||||
|
const type = dataJson.list[i].type;
|
||||||
|
if (type === "carousel2") {
|
||||||
|
carouselLarge.value = true;
|
||||||
|
} else if (type === "carousel1") {
|
||||||
|
carouselLarge.value = true;
|
||||||
|
carouselOpacity.value = true;
|
||||||
|
} else if (type === "seckill") {
|
||||||
|
dataJson.list[i].options.list = await getListByDay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
modelForm.value = dataJson;
|
||||||
|
storage.setItem("navList", dataJson.list[1]);
|
||||||
|
showNav.value = true;
|
||||||
|
topAdvert.value = dataJson.list[0];
|
||||||
|
}
|
||||||
|
|
||||||
checkStoreCollectionStatus() {
|
async function getListByDay() {
|
||||||
const storeId = this.storeMsg && this.storeMsg.storeId;
|
const res = await seckillByDay();
|
||||||
if (!storeId || !this.Cookies.getItem("userInfo")) {
|
if (res.success && res.result?.length) {
|
||||||
return;
|
return res.result;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkStoreCollectionStatus() {
|
||||||
|
const storeId = storeMsg.value && storeMsg.value.storeId;
|
||||||
|
if (!storeId || !storage.getItem("userInfo")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isStoreCollection("STORE", storeId)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.success && res.result) {
|
||||||
|
storeCollected.value = true;
|
||||||
}
|
}
|
||||||
isStoreCollection("STORE", storeId)
|
})
|
||||||
.then((res) => {
|
.catch(() => {
|
||||||
if (res.success && res.result) {
|
// 收藏状态查询失败不影响店铺页展示,静默处理
|
||||||
this.storeCollected = true;
|
});
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.catch(() => {
|
function getStoreMsg() {
|
||||||
// 收藏状态查询失败不影响店铺页展示,静默处理
|
const storeId = route.query.id;
|
||||||
});
|
if (!storeId) {
|
||||||
},
|
Message.warning("店铺不存在");
|
||||||
getStoreMsg() {
|
return;
|
||||||
const storeId = this.$route.query.id;
|
}
|
||||||
if (!storeId) {
|
getDetailById(storeId)
|
||||||
Message.warning("店铺不存在");
|
.then((res) => {
|
||||||
return;
|
if (res.success && res.result) {
|
||||||
}
|
storeMsg.value = res.result;
|
||||||
getDetailById(storeId)
|
document.title = storeMsg.value.storeName || "店铺";
|
||||||
.then((res) => {
|
checkStoreCollectionStatus();
|
||||||
if (res.success && res.result) {
|
if (isFloorEnabled()) {
|
||||||
this.storeMsg = res.result;
|
getIndexData();
|
||||||
document.title = this.storeMsg.storeName || "店铺";
|
|
||||||
this.checkStoreCollectionStatus();
|
|
||||||
if (this.isFloorEnabled()) {
|
|
||||||
this.getIndexData();
|
|
||||||
} else {
|
|
||||||
this.loadStoreGoodsView();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.storeMsg = { storeId };
|
|
||||||
this.loadStoreGoodsView();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
this.storeMsg = { storeId };
|
|
||||||
this.loadStoreGoodsView();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getCateList() {
|
|
||||||
// 店铺分类
|
|
||||||
getCateById(this.$route.query.id).then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
this.cateList = res.result;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getGoodsList() {
|
|
||||||
// 商品信息
|
|
||||||
goodsList(this.params)
|
|
||||||
.then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
this.goodsList = res.result.records;
|
|
||||||
this.total = res.result.total;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.warn('获取商品列表失败', err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
goGoodsDetail(skuId, goodsId) {
|
|
||||||
// 跳转商品详情
|
|
||||||
let routeUrl = this.$router.resolve({
|
|
||||||
path: "/goodsDetail",
|
|
||||||
query: {skuId, goodsId},
|
|
||||||
});
|
|
||||||
window.open(routeUrl.href, "_blank");
|
|
||||||
},
|
|
||||||
search(val) {
|
|
||||||
// 搜索本店商品
|
|
||||||
this.params.keyword = val;
|
|
||||||
this.getGoodsList();
|
|
||||||
},
|
|
||||||
searchByCate(cate) {
|
|
||||||
// 搜索同分类下商品
|
|
||||||
this.params.storeCatId = cate.id;
|
|
||||||
this.params.pageNumber = 1;
|
|
||||||
this.cateName = cate.labelName;
|
|
||||||
this.getGoodsList();
|
|
||||||
},
|
|
||||||
isCateActive(cate) {
|
|
||||||
if (!cate || !this.params.storeCatId) return false;
|
|
||||||
if (this.params.storeCatId === cate.id) return true;
|
|
||||||
return Array.isArray(cate.children) && cate.children.some((sec) => sec.id === this.params.storeCatId);
|
|
||||||
},
|
|
||||||
formatGrade(grade) {
|
|
||||||
if (grade === null || grade === undefined || grade === "") return 100;
|
|
||||||
const num = Number(grade);
|
|
||||||
if (Number.isNaN(num)) return 100;
|
|
||||||
const fixed = Number(num.toFixed(1));
|
|
||||||
return fixed % 1 === 0 ? fixed.toFixed(0) : String(fixed);
|
|
||||||
},
|
|
||||||
// 分页 修改页码
|
|
||||||
changePageNum(val) {
|
|
||||||
this.params.pageNumber = val;
|
|
||||||
this.getGoodsList();
|
|
||||||
},
|
|
||||||
// 分页 修改页数
|
|
||||||
changePageSize(val) {
|
|
||||||
this.params.pageNumber = 1;
|
|
||||||
this.params.pageSize = val;
|
|
||||||
this.getGoodsList();
|
|
||||||
},
|
|
||||||
async collect() {
|
|
||||||
// 收藏店铺
|
|
||||||
const storeId = this.storeMsg && this.storeMsg.storeId;
|
|
||||||
if (!storeId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!this.Cookies.getItem("userInfo")) {
|
|
||||||
Message.warning("请先登录");
|
|
||||||
this.$router.push("/login");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
if (this.storeCollected) {
|
|
||||||
const cancel = await cancelStoreCollect("STORE", storeId);
|
|
||||||
if (cancel.success) {
|
|
||||||
Message.success("已取消收藏");
|
|
||||||
this.storeCollected = false;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
const collect = await collectStore("STORE", storeId);
|
loadStoreGoodsView();
|
||||||
if (collect.success || collect.code === 200) {
|
|
||||||
this.storeCollected = true;
|
|
||||||
Message.success(
|
|
||||||
"收藏店铺成功,可以前往个人中心我的收藏查看"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
const msg = err?.message || err?.data?.message || "";
|
|
||||||
if (msg.includes("重复收藏")) {
|
|
||||||
this.storeCollected = true;
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
storeMsg.value = { storeId };
|
||||||
|
loadStoreGoodsView();
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
storeMsg.value = { storeId };
|
||||||
|
loadStoreGoodsView();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCateList() {
|
||||||
|
// 店铺分类
|
||||||
|
getCateById(route.query.id).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
cateList.value = res.result;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGoodsList() {
|
||||||
|
// 商品信息
|
||||||
|
goodsListApi(params)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
goodsList.value = res.result.records;
|
||||||
|
total.value = res.result.total;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.warn('获取商品列表失败', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function goGoodsDetail(skuId, goodsId) {
|
||||||
|
// 跳转商品详情
|
||||||
|
let routeUrl = router.resolve({
|
||||||
|
path: "/goodsDetail",
|
||||||
|
query: {skuId, goodsId},
|
||||||
|
});
|
||||||
|
window.open(routeUrl.href, "_blank");
|
||||||
|
}
|
||||||
|
|
||||||
|
function search(val) {
|
||||||
|
// 搜索本店商品
|
||||||
|
params.keyword = val;
|
||||||
|
getGoodsList();
|
||||||
|
}
|
||||||
|
|
||||||
|
function searchByCate(cate) {
|
||||||
|
// 搜索同分类下商品
|
||||||
|
params.storeCatId = cate.id;
|
||||||
|
params.pageNumber = 1;
|
||||||
|
cateName.value = cate.labelName;
|
||||||
|
getGoodsList();
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCateActive(cate) {
|
||||||
|
if (!cate || !params.storeCatId) return false;
|
||||||
|
if (params.storeCatId === cate.id) return true;
|
||||||
|
return Array.isArray(cate.children) && cate.children.some((sec) => sec.id === params.storeCatId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatGrade(grade) {
|
||||||
|
if (grade === null || grade === undefined || grade === "") return 100;
|
||||||
|
const num = Number(grade);
|
||||||
|
if (Number.isNaN(num)) return 100;
|
||||||
|
const fixed = Number(num.toFixed(1));
|
||||||
|
return fixed % 1 === 0 ? fixed.toFixed(0) : String(fixed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页 修改页码
|
||||||
|
function changePageNum(val) {
|
||||||
|
params.pageNumber = val;
|
||||||
|
getGoodsList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页 修改页数
|
||||||
|
function changePageSize(val) {
|
||||||
|
params.pageNumber = 1;
|
||||||
|
params.pageSize = val;
|
||||||
|
getGoodsList();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function collect() {
|
||||||
|
// 收藏店铺
|
||||||
|
const storeId = storeMsg.value && storeMsg.value.storeId;
|
||||||
|
if (!storeId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!storage.getItem("userInfo")) {
|
||||||
|
Message.warning("请先登录");
|
||||||
|
router.push("/login");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (storeCollected.value) {
|
||||||
|
const cancel = await cancelStoreCollect("STORE", storeId);
|
||||||
|
if (cancel.success) {
|
||||||
|
Message.success("已取消收藏");
|
||||||
|
storeCollected.value = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const collect = await collectStore("STORE", storeId);
|
||||||
|
if (collect.success || collect.code === 200) {
|
||||||
|
storeCollected.value = true;
|
||||||
|
Message.success(
|
||||||
|
"收藏店铺成功,可以前往个人中心我的收藏查看"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
const msg = err?.message || err?.data?.message || "";
|
||||||
|
if (msg.includes("重复收藏")) {
|
||||||
|
storeCollected.value = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 原 talkIm mixin 的登录提示
|
||||||
|
function promptLogin() {
|
||||||
|
Modal.confirm({
|
||||||
|
title: "温馨提示",
|
||||||
|
content: "请登录后执行此操作",
|
||||||
|
okText: "立即登录",
|
||||||
|
cancelText: "取消",
|
||||||
|
onOk: () => {
|
||||||
|
router.push({
|
||||||
|
path: "/login",
|
||||||
|
query: {
|
||||||
|
rePath: route.path,
|
||||||
|
query: JSON.stringify(route.query || {}),
|
||||||
|
},
|
||||||
|
});
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取im信息(原 talkIm mixin)
|
||||||
|
async function getIMDetailMethods() {
|
||||||
|
let res = await getIMDetail();
|
||||||
|
if (res.success) {
|
||||||
|
IMLink.value = res.result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳转im客服(原 talkIm mixin)
|
||||||
|
async function IMService(id, goodsId, skuId) {
|
||||||
|
if (!storage.getItem("accessToken")) {
|
||||||
|
promptLogin();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await getIMDetailMethods();
|
||||||
|
const userInfo = await getMemberMsg();
|
||||||
|
if (!userInfo.success) {
|
||||||
|
promptLogin();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const accessToken = storage.getItem("accessToken");
|
||||||
|
const storeId = id || storeMsg.value?.storeId;
|
||||||
|
const baseUrl = `${IMLink.value}?token=${accessToken}&id=${storeId}`;
|
||||||
|
|
||||||
|
if (goodsId && skuId) {
|
||||||
|
window.open(`${baseUrl}&goodsId=${goodsId}&skuId=${skuId}`);
|
||||||
|
} else {
|
||||||
|
window.open(baseUrl);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// token 过期等场景由 request 拦截器处理;未登录时避免未捕获异常
|
||||||
|
if (!storage.getItem("accessToken")) {
|
||||||
|
promptLogin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getStoreMsg();
|
||||||
|
window.onscroll = () => {
|
||||||
|
const top =
|
||||||
|
document.documentElement.scrollTop || document.body.scrollTop;
|
||||||
|
topSearchShow.value = top > 300;
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -11,10 +11,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
defineOptions({ name: "NotFound" });
|
||||||
name: "NotFound",
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -45,23 +45,28 @@ export default {
|
|||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
next();
|
next();
|
||||||
},
|
},
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
detail: {} // 商品详情
|
|
||||||
};
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
const { skuId, goodsId, thumbnail, goodsName } = this.$route.query;
|
|
||||||
this.detail = {
|
|
||||||
skuId,
|
|
||||||
goodsId,
|
|
||||||
thumbnail,
|
|
||||||
goodsName
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, ref } from "vue";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const detail = ref({}); // 商品详情
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const { skuId, goodsId, thumbnail, goodsName } = route.query;
|
||||||
|
detail.value = {
|
||||||
|
skuId,
|
||||||
|
goodsId,
|
||||||
|
thumbnail,
|
||||||
|
goodsName
|
||||||
|
};
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
/****************************加入购物车页面开始*****************************/
|
/****************************加入购物车页面开始*****************************/
|
||||||
.add-info-box-container{
|
.add-info-box-container{
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<div class="top-content">
|
<div class="top-content">
|
||||||
<div class="top-inner">
|
<div class="top-inner">
|
||||||
<div class="logo-box">
|
<div class="logo-box">
|
||||||
<img :src="$store.state.logoImg" @click="$router.push('/')" />
|
<img :src="logoImg" @click="$router.push('/')" />
|
||||||
<div>欢迎注册</div>
|
<div>欢迎注册</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="to-login">已有账号?<a @click.stop="$router.push('login')">立即登录</a></div>
|
<div class="to-login">已有账号?<a @click.stop="$router.push('login')">立即登录</a></div>
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
/>
|
/>
|
||||||
<div class="signup-card">
|
<div class="signup-card">
|
||||||
<el-form
|
<el-form
|
||||||
ref="formRegist"
|
ref="formRegistRef"
|
||||||
:model="formRegist"
|
:model="formRegist"
|
||||||
:rules="ruleInline"
|
:rules="ruleInline"
|
||||||
class="signup-form"
|
class="signup-form"
|
||||||
@@ -91,8 +91,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { onMounted, reactive, ref, computed } from "vue";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
import * as RegExp from '@/plugins/RegExp.js';
|
import * as RegExp from '@/plugins/RegExp.js';
|
||||||
import { md5 } from '@/plugins/md5.js';
|
import { md5 } from '@/plugins/md5.js';
|
||||||
@@ -101,120 +102,126 @@ import { sendSms } from '@/api/common.js';
|
|||||||
import Verify from '@/components/verify';
|
import Verify from '@/components/verify';
|
||||||
import { User, Lock, Iphone, Document } from '@element-plus/icons-vue';
|
import { User, Lock, Iphone, Document } from '@element-plus/icons-vue';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
export default {
|
import { useAppStore } from '@/stores/app';
|
||||||
name: 'SignUp',
|
|
||||||
components: { Verify, User, Lock, Iphone, Document },
|
defineOptions({ name: 'SignUp' });
|
||||||
data () {
|
|
||||||
return {
|
const router = useRouter();
|
||||||
config,
|
const appStore = useAppStore();
|
||||||
year: new Date().getFullYear(),
|
const logoImg = computed(() => appStore.logoImg);
|
||||||
formRegist: {
|
|
||||||
// 注册表单
|
const year = new Date().getFullYear();
|
||||||
mobilePhone: '',
|
const formRegist = reactive({
|
||||||
code: '',
|
// 注册表单
|
||||||
username: '',
|
mobilePhone: '',
|
||||||
password: ''
|
code: '',
|
||||||
},
|
username: '',
|
||||||
ruleInline: {
|
password: ''
|
||||||
// 验证规则
|
});
|
||||||
username: [{ required: true, message: '请输入用户名' }],
|
const ruleInline = reactive({
|
||||||
password: [
|
// 验证规则
|
||||||
{ required: true, message: '请输入密码' },
|
username: [{ required: true, message: '请输入用户名' }],
|
||||||
{ type: 'string', min: 6, message: '密码不能少于6位' }
|
password: [
|
||||||
],
|
{ required: true, message: '请输入密码' },
|
||||||
mobilePhone: [
|
{ type: 'string', min: 6, message: '密码不能少于6位' }
|
||||||
{ required: true, message: '请输入手机号码' },
|
],
|
||||||
{
|
mobilePhone: [
|
||||||
pattern: RegExp.mobile,
|
{ required: true, message: '请输入手机号码' },
|
||||||
trigger: 'blur',
|
{
|
||||||
message: '请输入正确的手机号'
|
pattern: RegExp.mobile,
|
||||||
}
|
trigger: 'blur',
|
||||||
],
|
message: '请输入正确的手机号'
|
||||||
code: [{ required: true, message: '请输入手机验证码' }]
|
|
||||||
},
|
|
||||||
verifyStatus: false, // 是否验证通过
|
|
||||||
verifyType: 'REGISTER', // 验证状态
|
|
||||||
codeMsg: '发送验证码', // 提示文字
|
|
||||||
interval: '', // 定时器
|
|
||||||
time: 60 // 倒计时
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
hideVerify() {
|
|
||||||
this.$refs.verify?.close?.();
|
|
||||||
},
|
|
||||||
// 注册
|
|
||||||
handleRegist () {
|
|
||||||
this.$refs.formRegist.validate((valid) => {
|
|
||||||
if (!valid) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const data = JSON.parse(JSON.stringify(this.formRegist));
|
|
||||||
data.password = md5(data.password);
|
|
||||||
apiLogin.regist(data).then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
Message.success('注册成功!');
|
|
||||||
this.$router.push('login');
|
|
||||||
} else {
|
|
||||||
Message.warning(res.message || '注册失败');
|
|
||||||
}
|
|
||||||
}).catch((err) => {
|
|
||||||
console.warn('注册请求失败', err);
|
|
||||||
});
|
|
||||||
}).catch(() => {
|
|
||||||
// 表单校验使用回调模式,此 catch 仅为兜底
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 发送短信验证码
|
|
||||||
sendCode () {
|
|
||||||
if (this.time !== 60) return;
|
|
||||||
if (this.formRegist.mobilePhone === '') {
|
|
||||||
Message.warning('请先填写手机号');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!RegExp.mobile.test(this.formRegist.mobilePhone)) {
|
|
||||||
Message.warning('请输入正确的手机号');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.$refs.verify?.init();
|
|
||||||
},
|
|
||||||
dispatchSms () {
|
|
||||||
sendSms({
|
|
||||||
mobile: this.formRegist.mobilePhone,
|
|
||||||
verificationEnums: 'REGISTER'
|
|
||||||
}).then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
Message.success('验证码发送成功');
|
|
||||||
this.interval = setInterval(() => {
|
|
||||||
this.time--;
|
|
||||||
if (this.time === 0) {
|
|
||||||
this.time = 60;
|
|
||||||
this.codeMsg = '重新发送';
|
|
||||||
this.verifyStatus = false;
|
|
||||||
clearInterval(this.interval);
|
|
||||||
} else {
|
|
||||||
this.codeMsg = this.time;
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
} else {
|
|
||||||
this.verifyStatus = false;
|
|
||||||
Message.warning(res.message || '验证码发送失败');
|
|
||||||
}
|
|
||||||
}).catch(() => {
|
|
||||||
this.verifyStatus = false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
verifyChange (con) {
|
|
||||||
if (!con.status) return;
|
|
||||||
this.hideVerify();
|
|
||||||
this.verifyStatus = true;
|
|
||||||
this.dispatchSms();
|
|
||||||
}
|
}
|
||||||
},
|
],
|
||||||
mounted () {
|
code: [{ required: true, message: '请输入手机验证码' }]
|
||||||
this.$refs.formRegist.resetFields();
|
});
|
||||||
|
const verifyStatus = ref(false); // 是否验证通过
|
||||||
|
const verifyType = ref('REGISTER'); // 验证状态
|
||||||
|
const codeMsg = ref('发送验证码'); // 提示文字
|
||||||
|
const interval = ref(''); // 定时器
|
||||||
|
const time = ref(60); // 倒计时
|
||||||
|
|
||||||
|
const formRegistRef = ref(null);
|
||||||
|
const verify = ref(null);
|
||||||
|
|
||||||
|
function hideVerify() {
|
||||||
|
verify.value?.close?.();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注册
|
||||||
|
function handleRegist () {
|
||||||
|
formRegistRef.value.validate((valid) => {
|
||||||
|
if (!valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data = JSON.parse(JSON.stringify(formRegist));
|
||||||
|
data.password = md5(data.password);
|
||||||
|
apiLogin.regist(data).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
Message.success('注册成功!');
|
||||||
|
router.push('login');
|
||||||
|
} else {
|
||||||
|
Message.warning(res.message || '注册失败');
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
console.warn('注册请求失败', err);
|
||||||
|
});
|
||||||
|
}).catch(() => {
|
||||||
|
// 表单校验使用回调模式,此 catch 仅为兜底
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送短信验证码
|
||||||
|
function sendCode () {
|
||||||
|
if (time.value !== 60) return;
|
||||||
|
if (formRegist.mobilePhone === '') {
|
||||||
|
Message.warning('请先填写手机号');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
};
|
if (!RegExp.mobile.test(formRegist.mobilePhone)) {
|
||||||
|
Message.warning('请输入正确的手机号');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
verify.value?.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
function dispatchSms () {
|
||||||
|
sendSms({
|
||||||
|
mobile: formRegist.mobilePhone,
|
||||||
|
verificationEnums: 'REGISTER'
|
||||||
|
}).then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
Message.success('验证码发送成功');
|
||||||
|
interval.value = setInterval(() => {
|
||||||
|
time.value--;
|
||||||
|
if (time.value === 0) {
|
||||||
|
time.value = 60;
|
||||||
|
codeMsg.value = '重新发送';
|
||||||
|
verifyStatus.value = false;
|
||||||
|
clearInterval(interval.value);
|
||||||
|
} else {
|
||||||
|
codeMsg.value = time.value;
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
} else {
|
||||||
|
verifyStatus.value = false;
|
||||||
|
Message.warning(res.message || '验证码发送失败');
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
verifyStatus.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyChange (con) {
|
||||||
|
if (!con.status) return;
|
||||||
|
hideVerify();
|
||||||
|
verifyStatus.value = true;
|
||||||
|
dispatchSms();
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
formRegistRef.value.resetFields();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.sign-up {
|
.sign-up {
|
||||||
|
|||||||
@@ -4,33 +4,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { onMounted, ref } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
import Index from './Index.vue'
|
import Index from './Index.vue'
|
||||||
import { getTopicData } from '@/api/index'
|
import { getTopicData } from '@/api/index'
|
||||||
export default {
|
|
||||||
data() {
|
const route = useRoute()
|
||||||
return {
|
|
||||||
pageData: ""
|
const pageData = ref("")
|
||||||
}
|
|
||||||
},
|
async function init(id) {
|
||||||
components: { Index },
|
const res = await getTopicData(id)
|
||||||
mounted() {
|
if (res.success) {
|
||||||
// 接收id进行查询
|
// 直接复用首页就行
|
||||||
const id = this.$route.query.id
|
if (res.result.pageData) {
|
||||||
this.init(id)
|
pageData.value = JSON.parse(res.result.pageData)
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async init(id) {
|
|
||||||
const res = await getTopicData(id)
|
|
||||||
if (res.success) {
|
|
||||||
// 直接复用首页就行
|
|
||||||
if (res.result.pageData) {
|
|
||||||
this.pageData = JSON.parse(res.result.pageData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 接收id进行查询
|
||||||
|
const id = route.query.id
|
||||||
|
init(id)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
|||||||
@@ -10,35 +10,36 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { onMounted, ref } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { articleDetail } from "@/api/common.js";
|
import { articleDetail } from "@/api/common.js";
|
||||||
import config from "@/config/index";
|
import config from "@/config/index";
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
config,
|
|
||||||
detail: "",
|
|
||||||
};
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.init();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
back() {
|
|
||||||
this.$router.push("/");
|
|
||||||
},
|
|
||||||
// 初始化文章
|
|
||||||
init() {
|
|
||||||
const id = this.$route.query.id;
|
|
||||||
|
|
||||||
articleDetail(id).then((res) => {
|
const route = useRoute();
|
||||||
if (res.success) {
|
const router = useRouter();
|
||||||
this.detail = res.result;
|
|
||||||
}
|
const configData = ref(config);
|
||||||
});
|
const detail = ref("");
|
||||||
},
|
|
||||||
},
|
function back() {
|
||||||
};
|
router.push("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化文章
|
||||||
|
function init() {
|
||||||
|
const id = route.query.id;
|
||||||
|
|
||||||
|
articleDetail(id).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
detail.value = res.result;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
init();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -48,87 +48,89 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import { articleCateList, articleDetail, articleList } from '@/api/common.js'
|
import { onMounted, reactive, ref } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { articleCateList, articleDetail, articleList as fetchArticleList } from '@/api/common.js'
|
||||||
|
|
||||||
export default {
|
defineOptions({ name: 'ArticleIndex' })
|
||||||
name: 'ArticleIndex',
|
|
||||||
data () {
|
const route = useRoute()
|
||||||
return {
|
|
||||||
list: [],
|
const list = ref([])
|
||||||
detail: '',
|
const detail = ref('')
|
||||||
articleList: [],
|
const articleList = ref([])
|
||||||
activeName: '1347456734864367616',
|
const activeName = ref('1347456734864367616')
|
||||||
openName: [],
|
const openName = ref([])
|
||||||
menuKey: 0,
|
const menuKey = ref(0)
|
||||||
params: {
|
const params = reactive({
|
||||||
pageNumber: 1,
|
pageNumber: 1,
|
||||||
pageSize: 100,
|
pageSize: 100,
|
||||||
categoryId: '',
|
categoryId: '',
|
||||||
sort: 'sort'
|
sort: 'sort'
|
||||||
},
|
})
|
||||||
showList: true
|
const showList = ref(true)
|
||||||
};
|
|
||||||
},
|
function onSelect (id) {
|
||||||
methods: {
|
getList(id)
|
||||||
onSelect (id) {
|
detail.value = ''
|
||||||
this.getList(id)
|
showList.value = true
|
||||||
this.detail = ''
|
}
|
||||||
this.showList = true
|
|
||||||
},
|
function getCateList () {
|
||||||
getCateList () {
|
articleCateList().then(res => {
|
||||||
articleCateList().then(res => {
|
if (res.success) {
|
||||||
if (res.success) {
|
list.value = res.result || []
|
||||||
this.list = res.result || []
|
openName.value = []
|
||||||
this.openName = []
|
if (route.query.id && detail.value && detail.value.categoryId) {
|
||||||
if (this.$route.query.id && this.detail && this.detail.categoryId) {
|
activeName.value = detail.value.categoryId
|
||||||
this.activeName = this.detail.categoryId
|
list.value.forEach(e => {
|
||||||
this.list.forEach(e => {
|
const children = e.children || []
|
||||||
const children = e.children || []
|
const hitParent = String(e.id) === String(activeName.value)
|
||||||
const hitParent = String(e.id) === String(this.activeName)
|
const hitChild = children.some(i => i && String(i.id) === String(activeName.value))
|
||||||
const hitChild = children.some(i => i && String(i.id) === String(this.activeName))
|
if ((hitParent || hitChild) && children.length) {
|
||||||
if ((hitParent || hitChild) && children.length) {
|
openName.value.push(String(e.id))
|
||||||
this.openName.push(String(e.id))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else if (this.list[0] && this.list[0].id) {
|
|
||||||
this.activeName = this.list[0].id
|
|
||||||
if (this.list[0].children && this.list[0].children.length) {
|
|
||||||
this.openName.push(String(this.list[0].id))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.menuKey++
|
|
||||||
if (this.activeName) {
|
|
||||||
this.getList(this.activeName)
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
} else if (list.value[0] && list.value[0].id) {
|
||||||
|
activeName.value = list.value[0].id
|
||||||
|
if (list.value[0].children && list.value[0].children.length) {
|
||||||
|
openName.value.push(String(list.value[0].id))
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
},
|
menuKey.value++
|
||||||
getList (id) {
|
if (activeName.value) {
|
||||||
this.params.categoryId = id
|
getList(activeName.value)
|
||||||
articleList(this.params).then(res => {
|
}
|
||||||
if (res.success) {
|
|
||||||
this.articleList = res.result.records
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
async getDetail (id) {
|
|
||||||
await articleDetail(id).then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
this.detail = res.result
|
|
||||||
this.showList = false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
async mounted () {
|
}
|
||||||
const articleId = this.$route.query.id
|
|
||||||
if (articleId) {
|
function getList (id) {
|
||||||
await this.getDetail(articleId)
|
params.categoryId = id
|
||||||
|
fetchArticleList(params).then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
articleList.value = res.result.records
|
||||||
}
|
}
|
||||||
this.getCateList()
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getDetail (id) {
|
||||||
|
await articleDetail(id).then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
detail.value = res.result
|
||||||
|
showList.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const articleId = route.query.id
|
||||||
|
if (articleId) {
|
||||||
|
await getDetail(articleId)
|
||||||
}
|
}
|
||||||
};
|
getCateList()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -44,88 +44,84 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { computed, onMounted, ref } from "vue";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
import { Bell } from "@element-plus/icons-vue";
|
import { Bell } from "@element-plus/icons-vue";
|
||||||
import storage from "@/plugins/storage";
|
import storage from "@/plugins/storage";
|
||||||
import { getOrderList } from "@/api/order";
|
import { getOrderList } from "@/api/order";
|
||||||
import { afterSaleList, getMembersWallet, memberCouponList } from "@/api/member";
|
import { afterSaleList, getMembersWallet, memberCouponList } from "@/api/member";
|
||||||
import { refreshUnreadMessage } from "@/utils/unreadMessage";
|
import { refreshUnreadMessage } from "@/utils/unreadMessage";
|
||||||
|
import { useAppStore } from "@/stores/app";
|
||||||
|
|
||||||
|
defineOptions({ name: "UserCenterHome" });
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const appStore = useAppStore();
|
||||||
|
|
||||||
function totalOf(res) {
|
function totalOf(res) {
|
||||||
return res && res.success ? Number(res.result && res.result.total) || 0 : 0;
|
return res && res.success ? Number(res.result && res.result.total) || 0 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
const displayName = ref("会员");
|
||||||
name: "UserCenterHome",
|
const waitPay = ref(0);
|
||||||
components: { Bell },
|
const waitShip = ref(0);
|
||||||
data() {
|
const waitReceive = ref(0);
|
||||||
return {
|
const afterSale = ref(0);
|
||||||
displayName: "会员",
|
const couponCount = ref(0);
|
||||||
waitPay: 0,
|
const balance = ref(0);
|
||||||
waitShip: 0,
|
|
||||||
waitReceive: 0,
|
const unreadMsg = computed(() => appStore.unreadMsg);
|
||||||
afterSale: 0,
|
const unreadLabel = computed(() => (unreadMsg.value > 99 ? "99+" : String(unreadMsg.value)));
|
||||||
couponCount: 0,
|
const orderStats = computed(() => [
|
||||||
balance: 0,
|
{ key: "pay", label: "待付款", count: waitPay.value, action: () => goOrder("WAIT_PAY") },
|
||||||
};
|
{ key: "ship", label: "待发货", count: waitShip.value, action: () => goOrder("WAIT_SHIP") },
|
||||||
},
|
{ key: "receive", label: "待收货", count: waitReceive.value, action: () => goOrder("WAIT_USE") },
|
||||||
computed: {
|
{ key: "after", label: "售后", count: afterSale.value, action: () => go("AfterSale") },
|
||||||
unreadMsg() {
|
]);
|
||||||
return Number(this.$store.state.unreadMsg) || 0;
|
|
||||||
},
|
function readUser() {
|
||||||
unreadLabel() {
|
try {
|
||||||
return this.unreadMsg > 99 ? "99+" : String(this.unreadMsg);
|
const raw = storage.getItem("userInfo");
|
||||||
},
|
const user = raw ? JSON.parse(raw) : {};
|
||||||
orderStats() {
|
displayName.value = user.nickName || user.username || "会员";
|
||||||
return [
|
} catch {
|
||||||
{ key: "pay", label: "待付款", count: this.waitPay, action: () => this.goOrder("WAIT_PAY") },
|
displayName.value = "会员";
|
||||||
{ key: "ship", label: "待发货", count: this.waitShip, action: () => this.goOrder("WAIT_SHIP") },
|
}
|
||||||
{ key: "receive", label: "待收货", count: this.waitReceive, action: () => this.goOrder("WAIT_USE") },
|
}
|
||||||
{ key: "after", label: "售后", count: this.afterSale, action: () => this.go("AfterSale") },
|
|
||||||
];
|
function loadSummary() {
|
||||||
},
|
const page = { pageNumber: 1, pageSize: 1 };
|
||||||
},
|
Promise.all([
|
||||||
mounted() {
|
getOrderList({ ...page, tag: "WAIT_PAY" }).catch(() => null),
|
||||||
this.readUser();
|
getOrderList({ ...page, tag: "WAIT_SHIP" }).catch(() => null),
|
||||||
this.loadSummary();
|
getOrderList({ ...page, tag: "WAIT_USE" }).catch(() => null),
|
||||||
},
|
afterSaleList({ ...page, sort: "createTime", order: "desc" }).catch(() => null),
|
||||||
methods: {
|
memberCouponList({ ...page, memberCouponStatus: "NEW" }).catch(() => null),
|
||||||
readUser() {
|
getMembersWallet().catch(() => null),
|
||||||
try {
|
refreshUnreadMessage(),
|
||||||
const raw = storage.getItem("userInfo");
|
]).then(([pay, ship, receive, afterSaleRes, coupon, wallet]) => {
|
||||||
const user = raw ? JSON.parse(raw) : {};
|
waitPay.value = totalOf(pay);
|
||||||
this.displayName = user.nickName || user.username || "会员";
|
waitShip.value = totalOf(ship);
|
||||||
} catch {
|
waitReceive.value = totalOf(receive);
|
||||||
this.displayName = "会员";
|
afterSale.value = totalOf(afterSaleRes);
|
||||||
}
|
couponCount.value = totalOf(coupon);
|
||||||
},
|
balance.value = wallet && wallet.success ? wallet.result.memberWallet || 0 : 0;
|
||||||
loadSummary() {
|
});
|
||||||
const page = { pageNumber: 1, pageSize: 1 };
|
}
|
||||||
Promise.all([
|
|
||||||
getOrderList({ ...page, tag: "WAIT_PAY" }).catch(() => null),
|
function go(name) {
|
||||||
getOrderList({ ...page, tag: "WAIT_SHIP" }).catch(() => null),
|
router.push({ name });
|
||||||
getOrderList({ ...page, tag: "WAIT_USE" }).catch(() => null),
|
}
|
||||||
afterSaleList({ ...page, sort: "createTime", order: "desc" }).catch(() => null),
|
|
||||||
memberCouponList({ ...page, memberCouponStatus: "NEW" }).catch(() => null),
|
function goOrder(tag) {
|
||||||
getMembersWallet().catch(() => null),
|
router.push({ name: "MyOrder", query: { tag } });
|
||||||
refreshUnreadMessage(),
|
}
|
||||||
]).then(([pay, ship, receive, afterSale, coupon, wallet]) => {
|
|
||||||
this.waitPay = totalOf(pay);
|
onMounted(() => {
|
||||||
this.waitShip = totalOf(ship);
|
readUser();
|
||||||
this.waitReceive = totalOf(receive);
|
loadSummary();
|
||||||
this.afterSale = totalOf(afterSale);
|
});
|
||||||
this.couponCount = totalOf(coupon);
|
|
||||||
this.balance = wallet && wallet.success ? wallet.result.memberWallet || 0 : 0;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
go(name) {
|
|
||||||
this.$router.push({ name });
|
|
||||||
},
|
|
||||||
goOrder(tag) {
|
|
||||||
this.$router.push({ name: "MyOrder", query: { tag } });
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -77,191 +77,187 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { computed, nextTick, onMounted, reactive, ref } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
import { Message, Modal } from "@/utils/message";
|
import { Message, Modal } from "@/utils/message";
|
||||||
import { Key, Link } from '@element-plus/icons-vue';
|
import { Key, Link } from '@element-plus/icons-vue';
|
||||||
// import { getPwdStatus } from '@/api/account';
|
// import { getPwdStatus } from '@/api/account';
|
||||||
import storage from '@/plugins/storage'
|
import storage from '@/plugins/storage'
|
||||||
import { bindThirdAccount, getThirdAccountBindList, getWechatH5QrCode, unbindThirdAccount } from '@/api/login.js'
|
import { bindThirdAccount, getThirdAccountBindList, getWechatH5QrCode, unbindThirdAccount } from '@/api/login.js'
|
||||||
export default {
|
|
||||||
components: { Key, Link },
|
|
||||||
name: 'AccountSafe',
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
pwdStatus: '',
|
|
||||||
connectLoading: false,
|
|
||||||
bindModalVisible: false,
|
|
||||||
bindSubmitLoading: false,
|
|
||||||
connectBoundTypes: [],
|
|
||||||
thirdAccountTypes: [
|
|
||||||
{ type: 'WECHAT', label: '微信' }
|
|
||||||
],
|
|
||||||
qrCodeLoading: false,
|
|
||||||
qrCodeBase64: '',
|
|
||||||
bindForm: {
|
|
||||||
type: '',
|
|
||||||
typeLabel: '',
|
|
||||||
unionID: ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
wechatConnect () {
|
|
||||||
return this.thirdAccountTypes[0]
|
|
||||||
},
|
|
||||||
qrCodeSrc () {
|
|
||||||
if (!this.qrCodeBase64) return ''
|
|
||||||
if (this.qrCodeBase64.startsWith('data:image')) return this.qrCodeBase64
|
|
||||||
return `data:image/png;base64,${this.qrCodeBase64}`
|
|
||||||
},
|
|
||||||
bindModalOkText () {
|
|
||||||
if (this.bindForm.type === 'WECHAT') return '我已完成绑定'
|
|
||||||
return '确定'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
this.getPwdStatus()
|
|
||||||
this.refreshConnectBinds()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// 设置支付密码
|
|
||||||
goModifyPwd () {
|
|
||||||
this.$router.push({name: 'ModifyPwd', query: { status: 2 }})
|
|
||||||
},
|
|
||||||
modifyPwd () { // 修改密码
|
|
||||||
this.$router.push({name: 'ModifyPwd', query: { status: 1 }})
|
|
||||||
},
|
|
||||||
// 获取密码状态
|
|
||||||
getPwdStatus () {},
|
|
||||||
// getPwdStatus () {
|
|
||||||
// getPwdStatus().then(res => {
|
|
||||||
// if (res) {
|
|
||||||
// this.pwdStatus = '修改密码'
|
|
||||||
// } else {
|
|
||||||
// this.pwdStatus = '设置密码'
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
isBound (type) {
|
|
||||||
return this.connectBoundTypes.includes(type)
|
|
||||||
},
|
|
||||||
refreshConnectBinds () {
|
|
||||||
this.connectLoading = true
|
|
||||||
return getThirdAccountBindList()
|
|
||||||
.then(res => {
|
|
||||||
if (res && res.success) {
|
|
||||||
this.connectBoundTypes = Array.isArray(res.result) ? res.result : []
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.connectLoading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
openBindModal (item) {
|
|
||||||
this.bindForm.type = item.type
|
|
||||||
this.bindForm.typeLabel = item.label
|
|
||||||
this.bindForm.unionID = ''
|
|
||||||
this.qrCodeBase64 = ''
|
|
||||||
this.bindModalVisible = true
|
|
||||||
if (item.type === 'WECHAT') {
|
|
||||||
this.fetchWechatQrCode()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
resetBindForm () {
|
|
||||||
this.bindForm.type = ''
|
|
||||||
this.bindForm.typeLabel = ''
|
|
||||||
this.bindForm.unionID = ''
|
|
||||||
this.qrCodeLoading = false
|
|
||||||
this.qrCodeBase64 = ''
|
|
||||||
this.bindSubmitLoading = false
|
|
||||||
},
|
|
||||||
fetchWechatQrCode () {
|
|
||||||
if (this.bindForm.type !== 'WECHAT') return
|
|
||||||
const rawUserInfo = storage.getItem('userInfo')
|
|
||||||
const userInfo = rawUserInfo ? JSON.parse(rawUserInfo) : null
|
|
||||||
const userId = userInfo && userInfo.id ? String(userInfo.id) : ''
|
|
||||||
if (!userId) {
|
|
||||||
this.qrCodeBase64 = ''
|
|
||||||
Message.error('未获取到当前用户ID,请重新登录后重试')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.qrCodeLoading = true
|
|
||||||
getWechatH5QrCode({ scene: userId })
|
|
||||||
.then(res => {
|
|
||||||
if (res && res.success) {
|
|
||||||
this.qrCodeBase64 = res.result || ''
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.qrCodeBase64 = ''
|
|
||||||
Message.error((res && res.message) || '二维码获取失败')
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
this.qrCodeBase64 = ''
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.qrCodeLoading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
submitBind () {
|
|
||||||
if (this.bindSubmitLoading) return
|
|
||||||
if (this.bindForm.type === 'WECHAT' && !this.bindForm.unionID) {
|
|
||||||
this.bindSubmitLoading = true
|
|
||||||
this.refreshConnectBinds()
|
|
||||||
.then(() => {
|
|
||||||
if (this.isBound(this.bindForm.type)) {
|
|
||||||
Message.success('绑定成功')
|
|
||||||
this.bindModalVisible = false
|
|
||||||
this.resetBindForm()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Message.warning('暂未检测到绑定,请扫码后重试')
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.bindModalVisible = true
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.bindSubmitLoading = false
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!this.bindForm.type) {
|
|
||||||
Message.error('绑定类型不能为空')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!this.bindForm.unionID) {
|
|
||||||
Message.error('OpenID/UnionID 不能为空')
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.bindModalVisible = true
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.bindSubmitLoading = true
|
defineOptions({ name: 'AccountSafe' });
|
||||||
bindThirdAccount({ unionID: this.bindForm.unionID, type: this.bindForm.type })
|
|
||||||
.then(() => {
|
const router = useRouter();
|
||||||
Message.success('绑定成功')
|
|
||||||
this.bindModalVisible = false
|
const pwdStatus = ref('');
|
||||||
this.resetBindForm()
|
const connectLoading = ref(false);
|
||||||
this.refreshConnectBinds()
|
const bindModalVisible = ref(false);
|
||||||
})
|
const bindSubmitLoading = ref(false);
|
||||||
.finally(() => {
|
const connectBoundTypes = ref([]);
|
||||||
this.bindSubmitLoading = false
|
const thirdAccountTypes = ref([
|
||||||
})
|
{ type: 'WECHAT', label: '微信' }
|
||||||
},
|
]);
|
||||||
confirmUnbind (item) {
|
const qrCodeLoading = ref(false);
|
||||||
Modal.confirm({
|
const qrCodeBase64 = ref('');
|
||||||
title: '解绑确认',
|
const bindForm = reactive({
|
||||||
content: `<p>确定解绑 ${item.label} 吗?</p>`,
|
type: '',
|
||||||
onOk: () => {
|
typeLabel: '',
|
||||||
unbindThirdAccount({ type: item.type }).then(() => {
|
unionID: ''
|
||||||
Message.success('解绑成功')
|
});
|
||||||
this.refreshConnectBinds()
|
|
||||||
})
|
const wechatConnect = computed(() => thirdAccountTypes.value[0]);
|
||||||
}
|
const qrCodeSrc = computed(() => {
|
||||||
})
|
if (!qrCodeBase64.value) return ''
|
||||||
}
|
if (qrCodeBase64.value.startsWith('data:image')) return qrCodeBase64.value
|
||||||
|
return `data:image/png;base64,${qrCodeBase64.value}`
|
||||||
|
});
|
||||||
|
const bindModalOkText = computed(() => {
|
||||||
|
if (bindForm.type === 'WECHAT') return '我已完成绑定'
|
||||||
|
return '确定'
|
||||||
|
});
|
||||||
|
|
||||||
|
// 设置支付密码
|
||||||
|
function goModifyPwd () {
|
||||||
|
router.push({name: 'ModifyPwd', query: { status: 2 }})
|
||||||
|
}
|
||||||
|
function modifyPwd () { // 修改密码
|
||||||
|
router.push({name: 'ModifyPwd', query: { status: 1 }})
|
||||||
|
}
|
||||||
|
// 获取密码状态
|
||||||
|
function getPwdStatus () {}
|
||||||
|
// function getPwdStatus () {
|
||||||
|
// getPwdStatus().then(res => {
|
||||||
|
// if (res) {
|
||||||
|
// pwdStatus.value = '修改密码'
|
||||||
|
// } else {
|
||||||
|
// pwdStatus.value = '设置密码'
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
function isBound (type) {
|
||||||
|
return connectBoundTypes.value.includes(type)
|
||||||
|
}
|
||||||
|
function refreshConnectBinds () {
|
||||||
|
connectLoading.value = true
|
||||||
|
return getThirdAccountBindList()
|
||||||
|
.then(res => {
|
||||||
|
if (res && res.success) {
|
||||||
|
connectBoundTypes.value = Array.isArray(res.result) ? res.result : []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
connectLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function openBindModal (item) {
|
||||||
|
bindForm.type = item.type
|
||||||
|
bindForm.typeLabel = item.label
|
||||||
|
bindForm.unionID = ''
|
||||||
|
qrCodeBase64.value = ''
|
||||||
|
bindModalVisible.value = true
|
||||||
|
if (item.type === 'WECHAT') {
|
||||||
|
fetchWechatQrCode()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function resetBindForm () {
|
||||||
|
bindForm.type = ''
|
||||||
|
bindForm.typeLabel = ''
|
||||||
|
bindForm.unionID = ''
|
||||||
|
qrCodeLoading.value = false
|
||||||
|
qrCodeBase64.value = ''
|
||||||
|
bindSubmitLoading.value = false
|
||||||
|
}
|
||||||
|
function fetchWechatQrCode () {
|
||||||
|
if (bindForm.type !== 'WECHAT') return
|
||||||
|
const rawUserInfo = storage.getItem('userInfo')
|
||||||
|
const userInfo = rawUserInfo ? JSON.parse(rawUserInfo) : null
|
||||||
|
const userId = userInfo && userInfo.id ? String(userInfo.id) : ''
|
||||||
|
if (!userId) {
|
||||||
|
qrCodeBase64.value = ''
|
||||||
|
Message.error('未获取到当前用户ID,请重新登录后重试')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
qrCodeLoading.value = true
|
||||||
|
getWechatH5QrCode({ scene: userId })
|
||||||
|
.then(res => {
|
||||||
|
if (res && res.success) {
|
||||||
|
qrCodeBase64.value = res.result || ''
|
||||||
|
return
|
||||||
|
}
|
||||||
|
qrCodeBase64.value = ''
|
||||||
|
Message.error((res && res.message) || '二维码获取失败')
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
qrCodeBase64.value = ''
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
qrCodeLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function submitBind () {
|
||||||
|
if (bindSubmitLoading.value) return
|
||||||
|
if (bindForm.type === 'WECHAT' && !bindForm.unionID) {
|
||||||
|
bindSubmitLoading.value = true
|
||||||
|
refreshConnectBinds()
|
||||||
|
.then(() => {
|
||||||
|
if (isBound(bindForm.type)) {
|
||||||
|
Message.success('绑定成功')
|
||||||
|
bindModalVisible.value = false
|
||||||
|
resetBindForm()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Message.warning('暂未检测到绑定,请扫码后重试')
|
||||||
|
nextTick(() => {
|
||||||
|
bindModalVisible.value = true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
bindSubmitLoading.value = false
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!bindForm.type) {
|
||||||
|
Message.error('绑定类型不能为空')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!bindForm.unionID) {
|
||||||
|
Message.error('OpenID/UnionID 不能为空')
|
||||||
|
nextTick(() => {
|
||||||
|
bindModalVisible.value = true
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
bindSubmitLoading.value = true
|
||||||
|
bindThirdAccount({ unionID: bindForm.unionID, type: bindForm.type })
|
||||||
|
.then(() => {
|
||||||
|
Message.success('绑定成功')
|
||||||
|
bindModalVisible.value = false
|
||||||
|
resetBindForm()
|
||||||
|
refreshConnectBinds()
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
bindSubmitLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function confirmUnbind (item) {
|
||||||
|
Modal.confirm({
|
||||||
|
title: '解绑确认',
|
||||||
|
content: `<p>确定解绑 ${item.label} 吗?</p>`,
|
||||||
|
onOk: () => {
|
||||||
|
unbindThirdAccount({ type: item.type }).then(() => {
|
||||||
|
Message.success('解绑成功')
|
||||||
|
refreshConnectBinds()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getPwdStatus()
|
||||||
|
refreshConnectBinds()
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -50,52 +50,55 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import {evolutionList} from '@/api/order.js';
|
import { reactive, ref, onMounted } from 'vue';
|
||||||
export default {
|
import { useRouter } from 'vue-router';
|
||||||
name: 'CommentList',
|
import { evolutionList } from '@/api/order.js';
|
||||||
data () {
|
|
||||||
return {
|
defineOptions({ name: 'CommentList' });
|
||||||
commentWay: [`待评价`, `待追评`, `已评价`], // 评价分类
|
|
||||||
loading: false, // 加载状态
|
const router = useRouter();
|
||||||
list: [], // 评价列表
|
|
||||||
total: 0, // 评价总数
|
const commentWay = ref([`待评价`, `待追评`, `已评价`]); // 评价分类
|
||||||
params: { // 请求参数
|
const loading = ref(false); // 加载状态
|
||||||
pageNumber: 1,
|
const list = ref([]); // 评价列表
|
||||||
pageSize: 20
|
const total = ref(0); // 评价总数
|
||||||
}
|
const params = reactive({ // 请求参数
|
||||||
};
|
pageNumber: 1,
|
||||||
},
|
pageSize: 20
|
||||||
mounted () {
|
});
|
||||||
this.getList()
|
|
||||||
},
|
function getList () { // 获取评价列表
|
||||||
methods: {
|
evolutionList(params).then(res => {
|
||||||
getList () { // 获取评价列表
|
if (res.success) {
|
||||||
evolutionList(this.params).then(res => {
|
const records = res.result.records;
|
||||||
if (res.success) {
|
records.forEach(element => {
|
||||||
const list = res.result.records;
|
element.descriptionScore = Number(element.descriptionScore)
|
||||||
list.forEach(element => {
|
});
|
||||||
element.descriptionScore = Number(element.descriptionScore)
|
list.value = records;
|
||||||
});
|
total.value = res.result.total
|
||||||
this.list = list;
|
|
||||||
this.total = res.result.total
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
changePageNum (val) { // 修改页码
|
|
||||||
this.params.pageNumber = val;
|
|
||||||
this.getList()
|
|
||||||
},
|
|
||||||
changePageSize (val) { // 修改页数
|
|
||||||
this.params.pageNumber = 1;
|
|
||||||
this.params.pageSize = val;
|
|
||||||
this.getList()
|
|
||||||
},
|
|
||||||
evaluateDetail (id) { // 跳转评价详情
|
|
||||||
this.$router.push({path: '/home/evalDetail', query: { id }})
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
|
function changePageNum (val) { // 修改页码
|
||||||
|
params.pageNumber = val;
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
function changePageSize (val) { // 修改页数
|
||||||
|
params.pageNumber = 1;
|
||||||
|
params.pageSize = val;
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
function evaluateDetail (id) { // 跳转评价详情
|
||||||
|
router.push({path: '/home/evalDetail', query: { id }})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getList()
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -129,75 +129,74 @@
|
|||||||
</UserCenterLayout>
|
</UserCenterLayout>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
|
import { onMounted, reactive, ref } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
import { View } from '@element-plus/icons-vue';
|
import { View } from '@element-plus/icons-vue';
|
||||||
import {getComplainDetail} from '@/api/member.js';
|
import {getComplainDetail} from '@/api/member.js';
|
||||||
import {communication} from '@/api/order';
|
import {communication} from '@/api/order';
|
||||||
export default {
|
|
||||||
components: { View },
|
const route = useRoute();
|
||||||
data () {
|
const router = useRouter();
|
||||||
return {
|
|
||||||
detail: {}, // 评价详情
|
const detail = ref({}); // 评价详情
|
||||||
visible: false, // 图片预览
|
const visible = ref(false); // 图片预览
|
||||||
previewImage: '', // 预览图片地址
|
const previewImage = ref(''); // 预览图片地址
|
||||||
loading: false, // 加载状态
|
const loading = ref(false); // 加载状态
|
||||||
submitLoading: false, // 回复消息loading
|
const submitLoading = ref(false); // 回复消息loading
|
||||||
// 状态
|
// 状态
|
||||||
statusLabel: {
|
const statusLabel = {
|
||||||
NO_APPLY: '未申请',
|
NO_APPLY: '未申请',
|
||||||
APPLYING: '申请中',
|
APPLYING: '申请中',
|
||||||
COMPLETE: '已完成',
|
COMPLETE: '已完成',
|
||||||
EXPIRED: '已失效',
|
EXPIRED: '已失效',
|
||||||
CANCEL: '已取消',
|
CANCEL: '已取消',
|
||||||
NEW: '待审核'
|
NEW: '待审核'
|
||||||
},
|
};
|
||||||
// 商家回复内容
|
// 商家回复内容
|
||||||
params: {
|
const params = reactive({
|
||||||
content: '',
|
content: '',
|
||||||
complainId: ''
|
complainId: ''
|
||||||
}
|
});
|
||||||
}
|
|
||||||
},
|
function getDetail () { // 获取投诉详情
|
||||||
methods: {
|
getComplainDetail(route.query.id).then(res => {
|
||||||
getDetail () { // 获取投诉详情
|
if (res.success) detail.value = res.result
|
||||||
getComplainDetail(this.$route.query.id).then(res => {
|
})
|
||||||
if (res.success) this.detail = res.result
|
|
||||||
})
|
|
||||||
},
|
|
||||||
goGoodsDetail (skuId, goodsId) { // 跳转商品详情
|
|
||||||
let routerUrl = this.$router.resolve({
|
|
||||||
path: '/goodsDetail',
|
|
||||||
query: {skuId, goodsId}
|
|
||||||
})
|
|
||||||
window.open(routerUrl.href, '_blank')
|
|
||||||
},
|
|
||||||
handleView (name) { // 预览图片
|
|
||||||
this.previewImage = name;
|
|
||||||
this.visible = true;
|
|
||||||
},
|
|
||||||
// 回复消息
|
|
||||||
handleSubmit () {
|
|
||||||
if (this.params.content === '') {
|
|
||||||
Message.error('请填写对话内容');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.submitLoading = true;
|
|
||||||
this.params.complainId = this.$route.query.id;
|
|
||||||
communication(this.params).then((res) => {
|
|
||||||
this.submitLoading = false;
|
|
||||||
if (res.success) {
|
|
||||||
Message.success('对话成功');
|
|
||||||
this.params.content = '';
|
|
||||||
this.getDetail();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
this.getDetail()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
function goGoodsDetail (skuId, goodsId) { // 跳转商品详情
|
||||||
|
let routerUrl = router.resolve({
|
||||||
|
path: '/goodsDetail',
|
||||||
|
query: {skuId, goodsId}
|
||||||
|
})
|
||||||
|
window.open(routerUrl.href, '_blank')
|
||||||
|
}
|
||||||
|
function handleView (name) { // 预览图片
|
||||||
|
previewImage.value = name;
|
||||||
|
visible.value = true;
|
||||||
|
}
|
||||||
|
// 回复消息
|
||||||
|
function handleSubmit () {
|
||||||
|
if (params.content === '') {
|
||||||
|
Message.error('请填写对话内容');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
submitLoading.value = true;
|
||||||
|
params.complainId = route.query.id;
|
||||||
|
communication(params).then((res) => {
|
||||||
|
submitLoading.value = false;
|
||||||
|
if (res.success) {
|
||||||
|
Message.success('对话成功');
|
||||||
|
params.content = '';
|
||||||
|
getDetail();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getDetail()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.speak-way {
|
.speak-way {
|
||||||
|
|||||||
@@ -58,72 +58,75 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { reactive, ref, onMounted } from "vue";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
import { Message, Modal } from "@/utils/message";
|
import { Message, Modal } from "@/utils/message";
|
||||||
import {complainList, clearComplain} from '@/api/member.js';
|
import { complainList, clearComplain } from '@/api/member.js';
|
||||||
export default {
|
|
||||||
name: 'ComplainList',
|
defineOptions({ name: 'ComplainList' });
|
||||||
data () {
|
|
||||||
return {
|
const router = useRouter();
|
||||||
loading: false, // 加载状态
|
|
||||||
list: [], // 投诉列表
|
const loading = ref(false); // 加载状态
|
||||||
statusLabel: { // 投诉状态
|
const list = ref([]); // 投诉列表
|
||||||
NO_APPLY: '未申请',
|
const statusLabel = { // 投诉状态
|
||||||
APPLYING: '申请中',
|
NO_APPLY: '未申请',
|
||||||
COMPLETE: '已完成',
|
APPLYING: '申请中',
|
||||||
EXPIRED: '已失效',
|
COMPLETE: '已完成',
|
||||||
CANCEL: '已取消',
|
EXPIRED: '已失效',
|
||||||
NEW: '新订单'
|
CANCEL: '已取消',
|
||||||
},
|
NEW: '新订单'
|
||||||
total: 0, // 投诉总数
|
};
|
||||||
params: { // 请求参数
|
const total = ref(0); // 投诉总数
|
||||||
pageNumber: 1,
|
const params = reactive({ // 请求参数
|
||||||
pageSize: 20
|
pageNumber: 1,
|
||||||
}
|
pageSize: 20
|
||||||
};
|
});
|
||||||
},
|
|
||||||
mounted () {
|
function getList() { // 获取投诉列表
|
||||||
this.getList()
|
complainList(params).then(res => {
|
||||||
},
|
if (res.success) {
|
||||||
methods: {
|
list.value = res.result.records;
|
||||||
getList () { // 获取投诉列表
|
total.value = res.result.total
|
||||||
complainList(this.params).then(res => {
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function changePageNum(val) { // 改变页码
|
||||||
|
params.pageNumber = val;
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
function changePageSize(val) { // 改变页数
|
||||||
|
params.pageNumber = 1;
|
||||||
|
params.pageSize = val;
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancel(id) { // 取消投诉
|
||||||
|
Modal.confirm({
|
||||||
|
title: '取消投诉',
|
||||||
|
content: '确定取消投诉吗?',
|
||||||
|
onOk: () => {
|
||||||
|
clearComplain(id).then((res) => {
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
const list = res.result.records;
|
Message.success('取消投诉成功');
|
||||||
this.list = list;
|
getList();
|
||||||
this.total = res.result.total
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
},
|
|
||||||
changePageNum (val) { // 改变页码
|
|
||||||
this.params.pageNumber = val;
|
|
||||||
this.getList()
|
|
||||||
},
|
|
||||||
changePageSize (val) { // 改变页数
|
|
||||||
this.params.pageNumber = 1;
|
|
||||||
this.params.pageSize = val;
|
|
||||||
this.getList()
|
|
||||||
},
|
|
||||||
cancel (id) { // 取消投诉
|
|
||||||
Modal.confirm({
|
|
||||||
title: '取消投诉',
|
|
||||||
content: '确定取消投诉吗?',
|
|
||||||
onOk: () => {
|
|
||||||
clearComplain(id).then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
Message.success('取消投诉成功');
|
|
||||||
this.getList();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onCancel: () => { }
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
goDetail (id) { // 跳转投诉详情
|
onCancel: () => { }
|
||||||
this.$router.push({path: '/home/complainDetail', query: { id }})
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
function goDetail(id) { // 跳转投诉详情
|
||||||
|
router.push({ path: '/home/complainDetail', query: { id } })
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getList()
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -84,136 +84,144 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { computed, onMounted, reactive, ref } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { Modal } from "@/utils/message";
|
import { Modal } from "@/utils/message";
|
||||||
import { collectList, cancelCollect, storeCollectList, cancelStoreCollect } from '@/api/member.js'
|
import { collectList, cancelCollect, storeCollectList, cancelStoreCollect } from '@/api/member.js'
|
||||||
export default {
|
|
||||||
name: 'Favorites',
|
|
||||||
props: {
|
|
||||||
homePage: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
favoriteWay: ['收藏商品', '收藏店铺'], // 收藏分类
|
|
||||||
list: [], // 收藏列表
|
|
||||||
total: 0, // 收藏总数
|
|
||||||
params: { // 请求参数
|
|
||||||
pageNumber: 1,
|
|
||||||
pageSize: 100,
|
|
||||||
type: this.$route.query.type || 'GOODS'
|
|
||||||
},
|
|
||||||
spinShow: false // 加载状态
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
activeTabIndex() {
|
|
||||||
return this.params.type === 'STORE' ? 1 : 0;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
defineOptions({ name: 'Favorites' });
|
||||||
loadFavorites() {
|
|
||||||
if (this.params.type === 'STORE') {
|
|
||||||
this.getStoreList();
|
|
||||||
} else {
|
|
||||||
this.getList();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getList() { // 获取商品收藏列表
|
|
||||||
this.spinShow = true
|
|
||||||
collectList(this.params).then(res => {
|
|
||||||
if (res.success && res.result) {
|
|
||||||
this.list = res.result.records || [];
|
|
||||||
}
|
|
||||||
}).catch(() => {
|
|
||||||
this.list = [];
|
|
||||||
}).finally(() => {
|
|
||||||
this.spinShow = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
getStoreList() { // 获取店铺收藏列表
|
|
||||||
this.spinShow = true
|
|
||||||
storeCollectList(this.params).then(res => {
|
|
||||||
if (res.success && res.result) {
|
|
||||||
this.list = res.result.records || [];
|
|
||||||
}
|
|
||||||
}).catch(() => {
|
|
||||||
this.list = [];
|
|
||||||
}).finally(() => {
|
|
||||||
this.spinShow = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
change(index) { // tab栏切换
|
|
||||||
if (index === 0) { this.params.type = 'GOODS', this.getList() }
|
|
||||||
if (index === 1) { this.params.type = 'STORE', this.getStoreList() }
|
|
||||||
},
|
|
||||||
cancel(id) { // 取消收藏
|
|
||||||
let typeName = this.params.type === 'GOODS' ? '商品' : '店铺'
|
|
||||||
Modal.confirm({
|
|
||||||
title: '取消收藏',
|
|
||||||
content: `确定取消收藏该${typeName}吗?`,
|
|
||||||
onOk: () => {
|
|
||||||
cancelCollect(this.params.type, id).then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
this.getList();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
cancelStore(id) { // 取消收藏
|
|
||||||
let typeName = this.params.type === 'GOODS' ? '商品' : '店铺'
|
|
||||||
Modal.confirm({
|
|
||||||
title: '取消收藏',
|
|
||||||
content: `确定取消收藏该${typeName}吗?`,
|
|
||||||
onOk: () => {
|
|
||||||
cancelStoreCollect(this.params.type, id).then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
this.getStoreList();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
openItem (item) {
|
|
||||||
if (this.params.type === 'STORE') {
|
|
||||||
this.goShop(item.id)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.buynow(item.skuId, item.goodsId)
|
|
||||||
},
|
|
||||||
buynow(skuId, goodsId,storeId) { // 跳转详情
|
|
||||||
let url
|
|
||||||
if (this.params.type === 'STORE') {
|
|
||||||
url = this.$router.resolve({
|
|
||||||
path: '/merchant',
|
|
||||||
query: { 'id': storeId }
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
url = this.$router.resolve({
|
|
||||||
path: '/goodsDetail',
|
|
||||||
query: { skuId, goodsId }
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
const props = defineProps({
|
||||||
window.open(url.href, '_blank')
|
homePage: {
|
||||||
|
type: Boolean,
|
||||||
},
|
default: false
|
||||||
goShop(id) { // 跳转店铺页面
|
|
||||||
this.$router.push({
|
|
||||||
path: '/merchant',
|
|
||||||
query: { id }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
if (this.homePage) this.params.pageSize = 5;
|
|
||||||
this.loadFavorites();
|
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const favoriteWay = ref(['收藏商品', '收藏店铺']); // 收藏分类
|
||||||
|
const list = ref([]); // 收藏列表
|
||||||
|
const total = ref(0); // 收藏总数
|
||||||
|
const params = reactive({ // 请求参数
|
||||||
|
pageNumber: 1,
|
||||||
|
pageSize: 100,
|
||||||
|
type: route.query.type || 'GOODS'
|
||||||
|
});
|
||||||
|
const spinShow = ref(false); // 加载状态
|
||||||
|
|
||||||
|
const activeTabIndex = computed(() => {
|
||||||
|
return params.type === 'STORE' ? 1 : 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
function loadFavorites() {
|
||||||
|
if (params.type === 'STORE') {
|
||||||
|
getStoreList();
|
||||||
|
} else {
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getList() { // 获取商品收藏列表
|
||||||
|
spinShow.value = true
|
||||||
|
collectList(params).then(res => {
|
||||||
|
if (res.success && res.result) {
|
||||||
|
list.value = res.result.records || [];
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
list.value = [];
|
||||||
|
}).finally(() => {
|
||||||
|
spinShow.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStoreList() { // 获取店铺收藏列表
|
||||||
|
spinShow.value = true
|
||||||
|
storeCollectList(params).then(res => {
|
||||||
|
if (res.success && res.result) {
|
||||||
|
list.value = res.result.records || [];
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
list.value = [];
|
||||||
|
}).finally(() => {
|
||||||
|
spinShow.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function change(index) { // tab栏切换
|
||||||
|
if (index === 0) { params.type = 'GOODS', getList() }
|
||||||
|
if (index === 1) { params.type = 'STORE', getStoreList() }
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancel(id) { // 取消收藏
|
||||||
|
let typeName = params.type === 'GOODS' ? '商品' : '店铺'
|
||||||
|
Modal.confirm({
|
||||||
|
title: '取消收藏',
|
||||||
|
content: `确定取消收藏该${typeName}吗?`,
|
||||||
|
onOk: () => {
|
||||||
|
cancelCollect(params.type, id).then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelStore(id) { // 取消收藏
|
||||||
|
let typeName = params.type === 'GOODS' ? '商品' : '店铺'
|
||||||
|
Modal.confirm({
|
||||||
|
title: '取消收藏',
|
||||||
|
content: `确定取消收藏该${typeName}吗?`,
|
||||||
|
onOk: () => {
|
||||||
|
cancelStoreCollect(params.type, id).then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
getStoreList();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function openItem (item) {
|
||||||
|
if (params.type === 'STORE') {
|
||||||
|
goShop(item.id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
buynow(item.skuId, item.goodsId)
|
||||||
|
}
|
||||||
|
|
||||||
|
function buynow(skuId, goodsId, storeId) { // 跳转详情
|
||||||
|
let url
|
||||||
|
if (params.type === 'STORE') {
|
||||||
|
url = router.resolve({
|
||||||
|
path: '/merchant',
|
||||||
|
query: { 'id': storeId }
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
url = router.resolve({
|
||||||
|
path: '/goodsDetail',
|
||||||
|
query: { skuId, goodsId }
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
window.open(url.href, '_blank')
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function goShop(id) { // 跳转店铺页面
|
||||||
|
router.push({
|
||||||
|
path: '/merchant',
|
||||||
|
query: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (props.homePage) params.pageSize = 5;
|
||||||
|
loadFavorites();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -74,206 +74,218 @@
|
|||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { computed, onMounted, reactive, ref } from 'vue'
|
||||||
import { memberGradeCurrent, memberGradeList, memberGradeRules } from '@/api/member.js'
|
import { memberGradeCurrent, memberGradeList, memberGradeRules } from '@/api/member.js'
|
||||||
|
|
||||||
export default {
|
defineOptions({ name: 'MemberGrade' })
|
||||||
name: 'MemberGrade',
|
|
||||||
data () {
|
const ruleList = ref([])
|
||||||
return {
|
const rulesLoading = ref(false)
|
||||||
ruleList: [],
|
const rulesDescription = ref('')
|
||||||
rulesLoading: false,
|
/** 接口返回 List MemberGradeDetailVO,每项 { grade, benefits } */
|
||||||
rulesDescription: '',
|
const gradeDetails = ref([])
|
||||||
/** 接口返回 List MemberGradeDetailVO,每项 { grade, benefits } */
|
const gradeList = ref([])
|
||||||
gradeDetails: [],
|
const currentGrade = ref({})
|
||||||
gradeList: [],
|
const currentExperience = ref(0)
|
||||||
currentGrade: {},
|
const currentGradeId = ref('')
|
||||||
currentExperience: 0,
|
const selectedRuleKey = ref('')
|
||||||
currentGradeId: '',
|
const activeGradeTab = ref('rules')
|
||||||
selectedRuleKey: '',
|
const logLoading = ref(false)
|
||||||
activeGradeTab: 'rules',
|
const gradeLoading = ref(false)
|
||||||
logLoading: false,
|
const params = reactive({
|
||||||
gradeLoading: false,
|
pageNumber: 1,
|
||||||
params: {
|
pageSize: 20
|
||||||
pageNumber: 1,
|
})
|
||||||
pageSize: 20
|
const experienceLogs = ref({})
|
||||||
},
|
|
||||||
experienceLogs: {}
|
const rulesMap = computed(() => {
|
||||||
}
|
const map = {}
|
||||||
},
|
ruleList.value.forEach(item => {
|
||||||
computed: {
|
map[item.ruleKey] = item.ruleName
|
||||||
rulesMap () {
|
})
|
||||||
const map = {}
|
return map
|
||||||
this.ruleList.forEach(item => {
|
})
|
||||||
map[item.ruleKey] = item.ruleName
|
|
||||||
})
|
const sortedGradeList = computed(() => {
|
||||||
return map
|
const list = Array.isArray(gradeList.value) ? gradeList.value.slice() : []
|
||||||
},
|
list.sort((a, b) => {
|
||||||
sortedGradeList () {
|
const sa = a && a.gradeSort != null ? Number(a.gradeSort) : 0
|
||||||
const list = Array.isArray(this.gradeList) ? this.gradeList.slice() : []
|
const sb = b && b.gradeSort != null ? Number(b.gradeSort) : 0
|
||||||
list.sort((a, b) => {
|
if (sa !== sb) return sa - sb
|
||||||
const sa = a && a.gradeSort != null ? Number(a.gradeSort) : 0
|
const ea = a && a.requiredExperience != null ? Number(a.requiredExperience) : 0
|
||||||
const sb = b && b.gradeSort != null ? Number(b.gradeSort) : 0
|
const eb = b && b.requiredExperience != null ? Number(b.requiredExperience) : 0
|
||||||
if (sa !== sb) return sa - sb
|
return ea - eb
|
||||||
const ea = a && a.requiredExperience != null ? Number(a.requiredExperience) : 0
|
})
|
||||||
const eb = b && b.requiredExperience != null ? Number(b.requiredExperience) : 0
|
return list
|
||||||
return ea - eb
|
})
|
||||||
})
|
|
||||||
return list
|
const currentGradeIndex = computed(() => {
|
||||||
},
|
if (!currentGradeId.value) return -1
|
||||||
currentGradeIndex () {
|
return sortedGradeList.value.findIndex(g => g && g.id === currentGradeId.value)
|
||||||
if (!this.currentGradeId) return -1
|
})
|
||||||
return this.sortedGradeList.findIndex(g => g && g.id === this.currentGradeId)
|
|
||||||
},
|
const nextGrade = computed(() => {
|
||||||
nextGrade () {
|
const idx = currentGradeIndex.value
|
||||||
const idx = this.currentGradeIndex
|
if (idx < 0) return null
|
||||||
if (idx < 0) return null
|
return sortedGradeList.value[idx + 1] || null
|
||||||
return this.sortedGradeList[idx + 1] || null
|
})
|
||||||
},
|
|
||||||
diffExperience () {
|
const diffExperience = computed(() => {
|
||||||
if (!this.nextGrade || this.nextGrade.requiredExperience == null) return 0
|
if (!nextGrade.value || nextGrade.value.requiredExperience == null) return 0
|
||||||
const diff = Number(this.nextGrade.requiredExperience) - Number(this.currentExperience || 0)
|
const diff = Number(nextGrade.value.requiredExperience) - Number(currentExperience.value || 0)
|
||||||
return diff > 0 ? diff : 0
|
return diff > 0 ? diff : 0
|
||||||
},
|
})
|
||||||
/** 当前等级在列表详情中对应的启用权益(顺序与后台 benefits 一致) */
|
|
||||||
currentGradeBenefits () {
|
/** 当前等级在列表详情中对应的启用权益(顺序与后台 benefits 一致) */
|
||||||
const id = this.currentGradeId
|
const currentGradeBenefits = computed(() => {
|
||||||
if (!id || !Array.isArray(this.gradeDetails) || !this.gradeDetails.length) return []
|
const id = currentGradeId.value
|
||||||
const detail = this.gradeDetails.find(
|
if (!id || !Array.isArray(gradeDetails.value) || !gradeDetails.value.length) return []
|
||||||
(d) => d && d.grade && String(d.grade.id) === String(id)
|
const detail = gradeDetails.value.find(
|
||||||
)
|
(d) => d && d.grade && String(d.grade.id) === String(id)
|
||||||
const list = detail && Array.isArray(detail.benefits) ? detail.benefits : []
|
)
|
||||||
return list.filter((b) => b && b.benefitState === 'OPEN')
|
const list = detail && Array.isArray(detail.benefits) ? detail.benefits : []
|
||||||
},
|
return list.filter((b) => b && b.benefitState === 'OPEN')
|
||||||
},
|
})
|
||||||
mounted () {
|
|
||||||
this.init()
|
onMounted(() => {
|
||||||
},
|
init()
|
||||||
methods: {
|
})
|
||||||
init () {
|
|
||||||
this.loadRules()
|
function init () {
|
||||||
this.loadGradeList()
|
loadRules()
|
||||||
this.loadCurrent()
|
loadGradeList()
|
||||||
},
|
loadCurrent()
|
||||||
loadRules () {
|
}
|
||||||
this.rulesLoading = true
|
|
||||||
memberGradeRules()
|
function loadRules () {
|
||||||
.then(res => {
|
rulesLoading.value = true
|
||||||
if (res && res.success) {
|
memberGradeRules()
|
||||||
const items = res.result && Array.isArray(res.result.items) ? res.result.items : []
|
.then(res => {
|
||||||
this.rulesDescription = (res.result && res.result.description) || ''
|
if (res && res.success) {
|
||||||
this.ruleList = items
|
const items = res.result && Array.isArray(res.result.items) ? res.result.items : []
|
||||||
}
|
rulesDescription.value = (res.result && res.result.description) || ''
|
||||||
})
|
ruleList.value = items
|
||||||
.finally(() => {
|
|
||||||
this.rulesLoading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
loadGradeList () {
|
|
||||||
this.gradeLoading = true
|
|
||||||
memberGradeList()
|
|
||||||
.then(res => {
|
|
||||||
if (res && res.success) {
|
|
||||||
const raw = Array.isArray(res.result) ? res.result : []
|
|
||||||
this.gradeDetails = raw
|
|
||||||
.map((item) => {
|
|
||||||
if (!item) return null
|
|
||||||
if (item.grade != null) {
|
|
||||||
return {
|
|
||||||
grade: item.grade,
|
|
||||||
benefits: Array.isArray(item.benefits) ? item.benefits : []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { grade: item, benefits: [] }
|
|
||||||
})
|
|
||||||
.filter(Boolean)
|
|
||||||
this.gradeList = this.gradeDetails.map((d) => d.grade).filter(Boolean)
|
|
||||||
} else {
|
|
||||||
this.gradeDetails = []
|
|
||||||
this.gradeList = []
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.gradeLoading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
loadCurrent () {
|
|
||||||
this.logLoading = true
|
|
||||||
const params = {
|
|
||||||
pageNumber: this.params.pageNumber,
|
|
||||||
pageSize: this.params.pageSize
|
|
||||||
}
|
}
|
||||||
if (this.selectedRuleKey) params.ruleKey = this.selectedRuleKey
|
})
|
||||||
memberGradeCurrent(params)
|
.finally(() => {
|
||||||
.then(res => {
|
rulesLoading.value = false
|
||||||
if (res && res.success && res.result) {
|
})
|
||||||
this.currentGrade = res.result.grade || {}
|
}
|
||||||
this.currentExperience = res.result.experience || 0
|
|
||||||
this.currentGradeId = res.result.gradeId || (this.currentGrade && this.currentGrade.id) || ''
|
function loadGradeList () {
|
||||||
this.experienceLogs = res.result.experienceLogs || {}
|
gradeLoading.value = true
|
||||||
}
|
memberGradeList()
|
||||||
})
|
.then(res => {
|
||||||
.finally(() => {
|
if (res && res.success) {
|
||||||
this.logLoading = false
|
const raw = Array.isArray(res.result) ? res.result : []
|
||||||
})
|
gradeDetails.value = raw
|
||||||
},
|
.map((item) => {
|
||||||
selectRule (ruleKey) {
|
if (!item) return null
|
||||||
this.selectedRuleKey = ruleKey || ''
|
if (item.grade != null) {
|
||||||
this.params.pageNumber = 1
|
return {
|
||||||
this.loadCurrent()
|
grade: item.grade,
|
||||||
},
|
benefits: Array.isArray(item.benefits) ? item.benefits : []
|
||||||
changePage (val) {
|
}
|
||||||
this.params.pageNumber = val
|
}
|
||||||
this.loadCurrent()
|
return { grade: item, benefits: [] }
|
||||||
},
|
})
|
||||||
changePageSize (val) {
|
.filter(Boolean)
|
||||||
this.params.pageSize = val
|
gradeList.value = gradeDetails.value.map((d) => d.grade).filter(Boolean)
|
||||||
this.params.pageNumber = 1
|
} else {
|
||||||
this.loadCurrent()
|
gradeDetails.value = []
|
||||||
},
|
gradeList.value = []
|
||||||
getRuleName (ruleKey) {
|
|
||||||
if (!ruleKey) return '-'
|
|
||||||
return this.rulesMap[ruleKey] || ruleKey
|
|
||||||
},
|
|
||||||
getRuleDesc (ruleKey) {
|
|
||||||
const map = {
|
|
||||||
CONSUME: '客户消费1元可获取经验值,向下取整',
|
|
||||||
REGISTER: '会员注册成功后可获取经验值',
|
|
||||||
SIGN_IN: '客户每日签到后可获取的经验值',
|
|
||||||
COMMENT: '对已购买商品完成发表评价获取经验值;仅针对评论字数大于30字的评论进行发放',
|
|
||||||
SHARE: '会员分享商城页面可获取的经验值',
|
|
||||||
PROFILE: '完善个人基本信息可获得经验值,每个会员仅可获得一次',
|
|
||||||
FOLLOW_STORE: '关注店铺可获得经验值,每个客户ID同店铺仅第一次关注可进行获得',
|
|
||||||
BIND_WECHAT: '绑定微信成功后获得经验值,每个会员仅可获得一次',
|
|
||||||
ADD_ADDRESS: '添加收货地址可获得经验值,每个会员仅可获得一次',
|
|
||||||
SHARE_REGISTER: '仅被注册成功后可获得相应奖励经验值',
|
|
||||||
SHARE_BUY: '仅被购买成功后可获得相应奖励经验值'
|
|
||||||
}
|
}
|
||||||
if (!ruleKey) return '-'
|
})
|
||||||
return map[ruleKey] || '-'
|
.finally(() => {
|
||||||
},
|
gradeLoading.value = false
|
||||||
getLogDeltaRaw (row) {
|
})
|
||||||
if (!row) return 0
|
}
|
||||||
const candidate = row.variableExperience != null ? row.variableExperience : (row.changeExperience != null ? row.changeExperience : (row.experience != null ? row.experience : row.value))
|
|
||||||
if (candidate == null) return 0
|
function loadCurrent () {
|
||||||
const num = Number(candidate)
|
logLoading.value = true
|
||||||
if (Number.isNaN(num)) return 0
|
const reqParams = {
|
||||||
if (row.type === 'DECREASE') return -Math.abs(num)
|
pageNumber: params.pageNumber,
|
||||||
if (row.type === 'INCREASE') return Math.abs(num)
|
pageSize: params.pageSize
|
||||||
return num
|
|
||||||
},
|
|
||||||
getLogDeltaSign (row) {
|
|
||||||
const num = this.getLogDeltaRaw(row)
|
|
||||||
return num >= 0 ? '+' : '-'
|
|
||||||
},
|
|
||||||
getLogDeltaAbsValue (row) {
|
|
||||||
const num = this.getLogDeltaRaw(row)
|
|
||||||
return Math.abs(num)
|
|
||||||
},
|
|
||||||
getLogDeltaColor (row) {
|
|
||||||
return this.getLogDeltaRaw(row) >= 0 ? 'green' : 'red'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (selectedRuleKey.value) reqParams.ruleKey = selectedRuleKey.value
|
||||||
|
memberGradeCurrent(reqParams)
|
||||||
|
.then(res => {
|
||||||
|
if (res && res.success && res.result) {
|
||||||
|
currentGrade.value = res.result.grade || {}
|
||||||
|
currentExperience.value = res.result.experience || 0
|
||||||
|
currentGradeId.value = res.result.gradeId || (currentGrade.value && currentGrade.value.id) || ''
|
||||||
|
experienceLogs.value = res.result.experienceLogs || {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
logLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectRule (ruleKey) {
|
||||||
|
selectedRuleKey.value = ruleKey || ''
|
||||||
|
params.pageNumber = 1
|
||||||
|
loadCurrent()
|
||||||
|
}
|
||||||
|
|
||||||
|
function changePage (val) {
|
||||||
|
params.pageNumber = val
|
||||||
|
loadCurrent()
|
||||||
|
}
|
||||||
|
|
||||||
|
function changePageSize (val) {
|
||||||
|
params.pageSize = val
|
||||||
|
params.pageNumber = 1
|
||||||
|
loadCurrent()
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRuleName (ruleKey) {
|
||||||
|
if (!ruleKey) return '-'
|
||||||
|
return rulesMap.value[ruleKey] || ruleKey
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRuleDesc (ruleKey) {
|
||||||
|
const map = {
|
||||||
|
CONSUME: '客户消费1元可获取经验值,向下取整',
|
||||||
|
REGISTER: '会员注册成功后可获取经验值',
|
||||||
|
SIGN_IN: '客户每日签到后可获取的经验值',
|
||||||
|
COMMENT: '对已购买商品完成发表评价获取经验值;仅针对评论字数大于30字的评论进行发放',
|
||||||
|
SHARE: '会员分享商城页面可获取的经验值',
|
||||||
|
PROFILE: '完善个人基本信息可获得经验值,每个会员仅可获得一次',
|
||||||
|
FOLLOW_STORE: '关注店铺可获得经验值,每个客户ID同店铺仅第一次关注可进行获得',
|
||||||
|
BIND_WECHAT: '绑定微信成功后获得经验值,每个会员仅可获得一次',
|
||||||
|
ADD_ADDRESS: '添加收货地址可获得经验值,每个会员仅可获得一次',
|
||||||
|
SHARE_REGISTER: '仅被注册成功后可获得相应奖励经验值',
|
||||||
|
SHARE_BUY: '仅被购买成功后可获得相应奖励经验值'
|
||||||
|
}
|
||||||
|
if (!ruleKey) return '-'
|
||||||
|
return map[ruleKey] || '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLogDeltaRaw (row) {
|
||||||
|
if (!row) return 0
|
||||||
|
const candidate = row.variableExperience != null ? row.variableExperience : (row.changeExperience != null ? row.changeExperience : (row.experience != null ? row.experience : row.value))
|
||||||
|
if (candidate == null) return 0
|
||||||
|
const num = Number(candidate)
|
||||||
|
if (Number.isNaN(num)) return 0
|
||||||
|
if (row.type === 'DECREASE') return -Math.abs(num)
|
||||||
|
if (row.type === 'INCREASE') return Math.abs(num)
|
||||||
|
return num
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLogDeltaSign (row) {
|
||||||
|
const num = getLogDeltaRaw(row)
|
||||||
|
return num >= 0 ? '+' : '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLogDeltaAbsValue (row) {
|
||||||
|
const num = getLogDeltaRaw(row)
|
||||||
|
return Math.abs(num)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLogDeltaColor (row) {
|
||||||
|
return getLogDeltaRaw(row) >= 0 ? 'green' : 'red'
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<UserCenterLayout>
|
<UserCenterLayout>
|
||||||
<!-- 手机号验证 -->
|
<!-- 手机号验证 -->
|
||||||
<el-form ref="formData" :model="formData" label-position="left" label-width="100px" :rules="ruleInLines"
|
<el-form ref="formDataRef" :model="formData" label-position="left" label-width="100px" :rules="ruleInLines"
|
||||||
v-if="(status == 2 || status == 3) && !showPwd">
|
v-if="(status == 2 || status == 3) && !showPwd">
|
||||||
<el-form-item label="手机号">
|
<el-form-item label="手机号">
|
||||||
<div class="phone">{{ displayMobile }}</div>
|
<div class="phone">{{ displayMobile }}</div>
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 修改 -->
|
<!-- 修改 -->
|
||||||
<el-form ref="formRegister" :model="formRegister" :rules="ruleInline" label-width="80px" v-if="status == 1 && !showPwd">
|
<el-form ref="formRegisterRef" :model="formRegister" :rules="ruleInline" label-width="80px" v-if="status == 1 && !showPwd">
|
||||||
<el-form-item label="旧密码" prop="password">
|
<el-form-item label="旧密码" prop="password">
|
||||||
<el-input type="password" v-model="formRegister.password" clearable size="large" placeholder="请输入旧密码"
|
<el-input type="password" v-model="formRegister.password" clearable size="large" placeholder="请输入旧密码"
|
||||||
style="width: 300px"
|
style="width: 300px"
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
<el-button type="primary" class="ml_10" @click="handleRegist">修改</el-button>
|
<el-button type="primary" class="ml_10" @click="handleRegist">修改</el-button>
|
||||||
</div>
|
</div>
|
||||||
<!-- 设置&修改的第二种情况 -->
|
<!-- 设置&修改的第二种情况 -->
|
||||||
<el-form ref="formDataUpdate" :model="formDataUpdate" label-position="left" label-width="100px" :rules="ruleIn"
|
<el-form ref="formDataUpdateRef" :model="formDataUpdate" label-position="left" label-width="100px" :rules="ruleIn"
|
||||||
v-if="showPwd">
|
v-if="showPwd">
|
||||||
<el-form-item label="新密码" prop="newPassword">
|
<el-form-item label="新密码" prop="newPassword">
|
||||||
<el-input type="password" v-model="formDataUpdate.newPassword" clearable size="large" placeholder="请输入新密码"
|
<el-input type="password" v-model="formDataUpdate.newPassword" clearable size="large" placeholder="请输入新密码"
|
||||||
@@ -73,7 +73,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { computed, onMounted, reactive, ref } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
import { Lock } from '@element-plus/icons-vue';
|
import { Lock } from '@element-plus/icons-vue';
|
||||||
import {
|
import {
|
||||||
@@ -82,166 +84,171 @@ import {
|
|||||||
} from '@/api/account';
|
} from '@/api/account';
|
||||||
import { getMemberMsg } from '@/api/login.js';
|
import { getMemberMsg } from '@/api/login.js';
|
||||||
import storage from '@/plugins/storage.js';
|
import storage from '@/plugins/storage.js';
|
||||||
import {md5} from '@/plugins/md5.js'
|
import { md5 } from '@/plugins/md5.js'
|
||||||
|
import { secrecyMobile } from '@/plugins/filters.js'
|
||||||
|
|
||||||
export default {
|
defineOptions({ name: 'modifyPwd' });
|
||||||
components: { Lock },
|
|
||||||
name: 'modifyPwd',
|
const route = useRoute();
|
||||||
data () {
|
const router = useRouter();
|
||||||
return {
|
|
||||||
// 1为修改 2为设置 3为提交校验的下一步
|
// 1为修改 2为设置 3为提交校验的下一步
|
||||||
status: '',
|
const status = ref('');
|
||||||
mobile: '',
|
const mobile = ref('');
|
||||||
showPwd: false, // 显示密码
|
const showPwd = ref(false); // 显示密码
|
||||||
formData: { // 验证表单
|
const formData = reactive({ // 验证表单
|
||||||
picture: '',
|
picture: '',
|
||||||
msg: ''
|
msg: ''
|
||||||
},
|
});
|
||||||
formDataUpdate: { // 直接输入新密码表单
|
const formDataUpdate = reactive({ // 直接输入新密码表单
|
||||||
newPassword: '',
|
newPassword: '',
|
||||||
againPassword: ''
|
againPassword: ''
|
||||||
},
|
});
|
||||||
formRegister: { // 第三步 新密码表单
|
const formRegister = reactive({ // 第三步 新密码表单
|
||||||
password: '',
|
password: '',
|
||||||
againPassword: '',
|
againPassword: '',
|
||||||
newPassword: ''
|
newPassword: ''
|
||||||
},
|
});
|
||||||
ruleInLines: { // 验证规则
|
const ruleInLines = { // 验证规则
|
||||||
picture: [
|
picture: [
|
||||||
{required: true, message: '请输入图片验证码', trigger: 'blur'}
|
{required: true, message: '请输入图片验证码', trigger: 'blur'}
|
||||||
],
|
],
|
||||||
msg: [
|
msg: [
|
||||||
{required: true, message: '请输入短信验证码', trigger: 'blur'}
|
{required: true, message: '请输入短信验证码', trigger: 'blur'}
|
||||||
]
|
]
|
||||||
},
|
};
|
||||||
ruleIn: { // 验证规则
|
const ruleIn = { // 验证规则
|
||||||
newPassword: [
|
newPassword: [
|
||||||
{required: true, message: '请输入新密码', trigger: 'blur'},
|
{required: true, message: '请输入新密码', trigger: 'blur'},
|
||||||
{type: 'string', min: 6, message: '密码不能少于6位'}
|
{type: 'string', min: 6, message: '密码不能少于6位'}
|
||||||
],
|
],
|
||||||
againPassword: [
|
againPassword: [
|
||||||
{required: true, message: '请输入新密码', trigger: 'blur'},
|
{required: true, message: '请输入新密码', trigger: 'blur'},
|
||||||
{type: 'string', min: 6, message: '密码不能少于6位'}
|
{type: 'string', min: 6, message: '密码不能少于6位'}
|
||||||
]
|
]
|
||||||
},
|
};
|
||||||
ruleInline: { // 验证规则
|
const ruleInline = { // 验证规则
|
||||||
password: [
|
password: [
|
||||||
{required: true, message: '请输入旧密码', trigger: 'blur'}
|
{required: true, message: '请输入旧密码', trigger: 'blur'}
|
||||||
],
|
],
|
||||||
newPassword: [
|
newPassword: [
|
||||||
{required: true, message: '请输入新密码', trigger: 'blur'},
|
{required: true, message: '请输入新密码', trigger: 'blur'},
|
||||||
{type: 'string', min: 6, message: '密码不能少于6位'}
|
{type: 'string', min: 6, message: '密码不能少于6位'}
|
||||||
],
|
],
|
||||||
againPassword: [
|
againPassword: [
|
||||||
{required: true, message: '请确认新密码', trigger: 'blur'},
|
{required: true, message: '请确认新密码', trigger: 'blur'},
|
||||||
{type: 'string', min: 6, message: '密码不能少于6位'}
|
{type: 'string', min: 6, message: '密码不能少于6位'}
|
||||||
]
|
]
|
||||||
}
|
};
|
||||||
}
|
|
||||||
},
|
const formDataRef = ref(null);
|
||||||
computed: {
|
const formRegisterRef = ref(null);
|
||||||
displayMobile () {
|
const formDataUpdateRef = ref(null);
|
||||||
if (!this.mobile) return '未绑定手机号';
|
|
||||||
return this.$filters.secrecyMobile(this.mobile);
|
const displayMobile = computed(() => {
|
||||||
}
|
if (!mobile.value) return '未绑定手机号';
|
||||||
},
|
return secrecyMobile(mobile.value);
|
||||||
mounted () {
|
});
|
||||||
this.status = this.$route.query.status
|
|
||||||
this.loadMobile()
|
function loadMobile () {
|
||||||
},
|
const rawUserInfo = storage.getItem('userInfo');
|
||||||
methods: {
|
if (rawUserInfo) {
|
||||||
loadMobile () {
|
try {
|
||||||
const rawUserInfo = storage.getItem('userInfo');
|
const userInfo = JSON.parse(rawUserInfo);
|
||||||
if (rawUserInfo) {
|
if (userInfo && userInfo.mobile) mobile.value = userInfo.mobile;
|
||||||
try {
|
} catch {
|
||||||
const userInfo = JSON.parse(rawUserInfo);
|
// 缓存的用户信息解析失败时忽略,改走接口获取
|
||||||
if (userInfo && userInfo.mobile) this.mobile = userInfo.mobile;
|
|
||||||
} catch {
|
|
||||||
// 缓存的用户信息解析失败时忽略,改走接口获取
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!storage.getItem('accessToken')) return;
|
|
||||||
getMemberMsg().then(res => {
|
|
||||||
if (res.success && res.result) {
|
|
||||||
storage.setItem('userInfo', res.result);
|
|
||||||
this.mobile = res.result.mobile || '';
|
|
||||||
}
|
|
||||||
}).catch((err) => {
|
|
||||||
console.warn('获取会员信息失败', err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
normalizePwdError (message) {
|
|
||||||
if (!message) return '修改失败,请稍后重试';
|
|
||||||
if (String(message).includes('旧密码') || String(message).includes('密码不正确')) {
|
|
||||||
return '旧密码不正确';
|
|
||||||
}
|
|
||||||
return message;
|
|
||||||
},
|
|
||||||
// 修改
|
|
||||||
handleRegist () {
|
|
||||||
this.$refs['formRegister'].validate((valid) => {
|
|
||||||
if (valid) {
|
|
||||||
const {newPassword, againPassword, password} = this.formRegister
|
|
||||||
if (newPassword !== againPassword) {
|
|
||||||
Message.error({
|
|
||||||
content: '新密码和确认密码不一致'
|
|
||||||
});
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const params = {newPassword, password}
|
|
||||||
params.newPassword = md5(newPassword)
|
|
||||||
params.password = md5(password)
|
|
||||||
editPwd(params).then(res => {
|
|
||||||
if (res.success && res.result) {
|
|
||||||
Message.success('修改密码成功');
|
|
||||||
this.$router.push('/home')
|
|
||||||
} else {
|
|
||||||
Message.error(this.normalizePwdError(res.message));
|
|
||||||
}
|
|
||||||
}).catch((err) => {
|
|
||||||
Message.error(this.normalizePwdError(err?.message || err?.data?.message));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// 提交验证
|
|
||||||
submitRegister () {
|
|
||||||
this.$refs['formData'].validate((valid) => {
|
|
||||||
if (valid) {
|
|
||||||
this.showPwd = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// 设置密码
|
|
||||||
setPassword () {
|
|
||||||
this.$refs['formDataUpdate'].validate((valid) => {
|
|
||||||
if (valid) {
|
|
||||||
const {newPassword, againPassword} = this.formDataUpdate
|
|
||||||
if (newPassword === '') {
|
|
||||||
Message.error({
|
|
||||||
content: '请输入密码'
|
|
||||||
});
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (newPassword !== againPassword) {
|
|
||||||
Message.error({
|
|
||||||
content: '新密码和确认密码不一致'
|
|
||||||
});
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const params = {password: newPassword}
|
|
||||||
params.password = md5(newPassword)
|
|
||||||
setPwd(params).then(res => {
|
|
||||||
if (res.message === 'success' && res.result) {
|
|
||||||
Message.success('支付密码设置成功');
|
|
||||||
this.$router.push('/home')
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!storage.getItem('accessToken')) return;
|
||||||
|
getMemberMsg().then(res => {
|
||||||
|
if (res.success && res.result) {
|
||||||
|
storage.setItem('userInfo', res.result);
|
||||||
|
mobile.value = res.result.mobile || '';
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
console.warn('获取会员信息失败', err);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizePwdError (message) {
|
||||||
|
if (!message) return '修改失败,请稍后重试';
|
||||||
|
if (String(message).includes('旧密码') || String(message).includes('密码不正确')) {
|
||||||
|
return '旧密码不正确';
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改
|
||||||
|
function handleRegist () {
|
||||||
|
formRegisterRef.value.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
const {newPassword, againPassword, password} = formRegister
|
||||||
|
if (newPassword !== againPassword) {
|
||||||
|
Message.error({
|
||||||
|
content: '新密码和确认密码不一致'
|
||||||
|
});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const params = {newPassword, password}
|
||||||
|
params.newPassword = md5(newPassword)
|
||||||
|
params.password = md5(password)
|
||||||
|
editPwd(params).then(res => {
|
||||||
|
if (res.success && res.result) {
|
||||||
|
Message.success('修改密码成功');
|
||||||
|
router.push('/home')
|
||||||
|
} else {
|
||||||
|
Message.error(normalizePwdError(res.message));
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
Message.error(normalizePwdError(err?.message || err?.data?.message));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交验证
|
||||||
|
function submitRegister () {
|
||||||
|
formDataRef.value.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
showPwd.value = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置密码
|
||||||
|
function setPassword () {
|
||||||
|
formDataUpdateRef.value.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
const {newPassword, againPassword} = formDataUpdate
|
||||||
|
if (newPassword === '') {
|
||||||
|
Message.error({
|
||||||
|
content: '请输入密码'
|
||||||
|
});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (newPassword !== againPassword) {
|
||||||
|
Message.error({
|
||||||
|
content: '新密码和确认密码不一致'
|
||||||
|
});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const params = {password: newPassword}
|
||||||
|
params.password = md5(newPassword)
|
||||||
|
setPwd(params).then(res => {
|
||||||
|
if (res.message === 'success' && res.result) {
|
||||||
|
Message.success('支付密码设置成功');
|
||||||
|
router.push('/home')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
status.value = route.query.status
|
||||||
|
loadMobile()
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -42,48 +42,47 @@
|
|||||||
</UserCenterLayout>
|
</UserCenterLayout>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
import {memberPoint, memberPointHistory} from '@/api/member.js'
|
import { onMounted, reactive, ref } from "vue";
|
||||||
|
import { memberPoint, memberPointHistory } from '@/api/member.js'
|
||||||
|
|
||||||
export default {
|
defineOptions({ name: "Point" });
|
||||||
name: 'Point',
|
|
||||||
data () {
|
const logData = ref({}); // 积分日志
|
||||||
return {
|
const pointObj = ref({}); // 积分明细
|
||||||
logData: {}, // 积分日志
|
const loading = ref(false); // 请求接口加载框
|
||||||
pointObj: {}, // 积分明细
|
const params = reactive({ // 积分列表请求参数
|
||||||
loading: false, // 请求接口加载框
|
pageNumber: 1,
|
||||||
params: { // 积分列表请求参数
|
pageSize: 20
|
||||||
pageNumber: 1,
|
});
|
||||||
pageSize: 20
|
|
||||||
},
|
function getHistory() { // 获取积分历史
|
||||||
}
|
memberPointHistory(params).then(res => {
|
||||||
},
|
logData.value = res.result;
|
||||||
mounted () {
|
})
|
||||||
this.getHistory()
|
|
||||||
this.getPoint()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getHistory () { // 获取积分历史
|
|
||||||
memberPointHistory(this.params).then(res => {
|
|
||||||
this.logData = res.result;
|
|
||||||
})
|
|
||||||
},
|
|
||||||
getPoint () { // 获取积分详情
|
|
||||||
memberPoint().then(res => {
|
|
||||||
if (res.success) this.pointObj = res.result
|
|
||||||
})
|
|
||||||
},
|
|
||||||
changePage (val) { // 修改页码
|
|
||||||
this.params.pageNumber = val
|
|
||||||
this.getHistory()
|
|
||||||
},
|
|
||||||
changePageSize (val) { // 修改页数
|
|
||||||
this.params.pageSize = val
|
|
||||||
this.params.pageNumber = 1
|
|
||||||
this.getHistory()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getPoint() { // 获取积分详情
|
||||||
|
memberPoint().then(res => {
|
||||||
|
if (res.success) pointObj.value = res.result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function changePage(val) { // 修改页码
|
||||||
|
params.pageNumber = val
|
||||||
|
getHistory()
|
||||||
|
}
|
||||||
|
|
||||||
|
function changePageSize(val) { // 修改页数
|
||||||
|
params.pageSize = val
|
||||||
|
params.pageNumber = 1
|
||||||
|
getHistory()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getHistory()
|
||||||
|
getPoint()
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
h3 {
|
h3 {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<UserCenterLayout>
|
<UserCenterLayout>
|
||||||
<el-form class="profile-form" :model="formItem" :rules="rules" ref="form" label-width="72px" label-position="left">
|
<el-form class="profile-form" :model="formItem" :rules="rules" ref="formRef" label-width="72px" label-position="left">
|
||||||
<el-form-item label="头像" class="avatar-form-item">
|
<el-form-item label="头像" class="avatar-form-item">
|
||||||
<div class="avatar-upload">
|
<div class="avatar-upload">
|
||||||
<el-avatar v-if="formItem.face" :src="formItem.face" :size="72" />
|
<el-avatar v-if="formItem.face" :src="formItem.face" :size="72" />
|
||||||
@@ -42,114 +42,115 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { onMounted, reactive, ref } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
import { editMemberInfo } from '@/api/account.js';
|
import { editMemberInfo } from '@/api/account.js';
|
||||||
import { getMemberMsg } from '@/api/login.js';
|
import { getMemberMsg } from '@/api/login.js';
|
||||||
import { buyerUrl } from '@/plugins/request.js';
|
import { buyerUrl } from '@/plugins/request.js';
|
||||||
import storage from '@/plugins/storage.js';
|
import storage from '@/plugins/storage.js';
|
||||||
import { User } from '@element-plus/icons-vue';
|
import { User } from '@element-plus/icons-vue';
|
||||||
export default {
|
|
||||||
components: { User },
|
defineOptions({ name: 'Profile' });
|
||||||
name: 'Profile',
|
|
||||||
data () {
|
const route = useRoute();
|
||||||
return {
|
const router = useRouter();
|
||||||
rules: { // 验证规则
|
|
||||||
nickName: [{required: true, message: '用户昵称不能为空'}, { max: 16, message: '用户昵称不能超过15个字符' }]
|
const formRef = ref(null);
|
||||||
},
|
|
||||||
formItem: {}, // 表单数据
|
const rules = { // 验证规则
|
||||||
action: buyerUrl + '/buyer/common/upload/file', // 上传接口
|
nickName: [{required: true, message: '用户昵称不能为空'}, { max: 16, message: '用户昵称不能超过15个字符' }]
|
||||||
accessToken: {} // 验证token
|
};
|
||||||
|
const formItem = ref({}); // 表单数据
|
||||||
|
const action = buyerUrl + '/buyer/common/upload/file'; // 上传接口
|
||||||
|
const accessToken = reactive({}); // 验证token
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
accessToken.accessToken = storage.getItem('accessToken');
|
||||||
|
loadUserInfo();
|
||||||
|
});
|
||||||
|
|
||||||
|
function loadUserInfo () {
|
||||||
|
const rawUserInfo = storage.getItem('userInfo');
|
||||||
|
if (rawUserInfo) {
|
||||||
|
applyUserInfo(JSON.parse(rawUserInfo));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!storage.getItem('accessToken')) {
|
||||||
|
redirectToLogin();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
getMemberMsg().then(res => {
|
||||||
|
if (res.success && res.result) {
|
||||||
|
storage.setItem('userInfo', res.result);
|
||||||
|
applyUserInfo(res.result);
|
||||||
|
} else {
|
||||||
|
redirectToLogin();
|
||||||
}
|
}
|
||||||
},
|
}).catch(() => {
|
||||||
mounted () {
|
redirectToLogin();
|
||||||
this.accessToken.accessToken = storage.getItem('accessToken');
|
});
|
||||||
this.loadUserInfo();
|
}
|
||||||
},
|
function applyUserInfo (userInfo) {
|
||||||
methods: {
|
formItem.value = { ...userInfo };
|
||||||
loadUserInfo () {
|
formItem.value.birthday = normalizeBirthday(formItem.value.birthday);
|
||||||
const rawUserInfo = storage.getItem('userInfo');
|
}
|
||||||
if (rawUserInfo) {
|
function redirectToLogin () {
|
||||||
this.applyUserInfo(JSON.parse(rawUserInfo));
|
router.push({
|
||||||
return;
|
path: '/login',
|
||||||
|
query: {
|
||||||
|
rePath: route.path,
|
||||||
|
query: JSON.stringify(route.query || {}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function normalizeBirthday (value) {
|
||||||
|
if (!value) return ''
|
||||||
|
if (value instanceof Date) return value
|
||||||
|
if (typeof value === 'number') return new Date(value)
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
const m = value.match(/^(\d{4})-(\d{2})-(\d{2})/)
|
||||||
|
if (m) return new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]))
|
||||||
|
const d = new Date(value)
|
||||||
|
return isNaN(d.getTime()) ? '' : d
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
function formatBirthday (value) {
|
||||||
|
if (!value) return null
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
const m = value.match(/^(\d{4})-(\d{2})-(\d{2})/)
|
||||||
|
return m ? `${m[1]}-${m[2]}-${m[3]}` : null
|
||||||
|
}
|
||||||
|
const d = value instanceof Date ? value : new Date(value)
|
||||||
|
if (isNaN(d.getTime())) return null
|
||||||
|
const yyyy = d.getFullYear()
|
||||||
|
const mm = String(d.getMonth() + 1).padStart(2, '0')
|
||||||
|
const dd = String(d.getDate()).padStart(2, '0')
|
||||||
|
return `${yyyy}-${mm}-${dd}`
|
||||||
|
}
|
||||||
|
function save () { // 保存
|
||||||
|
formRef.value.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
let params = {
|
||||||
|
birthday: formatBirthday(formItem.value.birthday),
|
||||||
|
face: formItem.value.face,
|
||||||
|
nickName: formItem.value.nickName,
|
||||||
|
sex: formItem.value.sex
|
||||||
}
|
}
|
||||||
if (!storage.getItem('accessToken')) {
|
editMemberInfo(params).then(res => {
|
||||||
this.redirectToLogin();
|
if (res.success) {
|
||||||
return;
|
Message.success('修改个人资料成功')
|
||||||
}
|
storage.setItem('userInfo', res.result)
|
||||||
getMemberMsg().then(res => {
|
router.go(0)
|
||||||
if (res.success && res.result) {
|
|
||||||
storage.setItem('userInfo', res.result);
|
|
||||||
this.applyUserInfo(res.result);
|
|
||||||
} else {
|
|
||||||
this.redirectToLogin();
|
|
||||||
}
|
|
||||||
}).catch(() => {
|
|
||||||
this.redirectToLogin();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
applyUserInfo (userInfo) {
|
|
||||||
this.formItem = { ...userInfo };
|
|
||||||
this.formItem.birthday = this.normalizeBirthday(this.formItem.birthday);
|
|
||||||
},
|
|
||||||
redirectToLogin () {
|
|
||||||
this.$router.push({
|
|
||||||
path: '/login',
|
|
||||||
query: {
|
|
||||||
rePath: this.$route.path,
|
|
||||||
query: JSON.stringify(this.$route.query || {}),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
normalizeBirthday (value) {
|
|
||||||
if (!value) return ''
|
|
||||||
if (value instanceof Date) return value
|
|
||||||
if (typeof value === 'number') return new Date(value)
|
|
||||||
if (typeof value === 'string') {
|
|
||||||
const m = value.match(/^(\d{4})-(\d{2})-(\d{2})/)
|
|
||||||
if (m) return new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]))
|
|
||||||
const d = new Date(value)
|
|
||||||
return isNaN(d.getTime()) ? '' : d
|
|
||||||
}
|
|
||||||
return ''
|
|
||||||
},
|
|
||||||
formatBirthday (value) {
|
|
||||||
if (!value) return null
|
|
||||||
if (typeof value === 'string') {
|
|
||||||
const m = value.match(/^(\d{4})-(\d{2})-(\d{2})/)
|
|
||||||
return m ? `${m[1]}-${m[2]}-${m[3]}` : null
|
|
||||||
}
|
|
||||||
const d = value instanceof Date ? value : new Date(value)
|
|
||||||
if (isNaN(d.getTime())) return null
|
|
||||||
const yyyy = d.getFullYear()
|
|
||||||
const mm = String(d.getMonth() + 1).padStart(2, '0')
|
|
||||||
const dd = String(d.getDate()).padStart(2, '0')
|
|
||||||
return `${yyyy}-${mm}-${dd}`
|
|
||||||
},
|
|
||||||
save () { // 保存
|
|
||||||
this.$refs.form.validate(valid => {
|
|
||||||
if (valid) {
|
|
||||||
let params = {
|
|
||||||
birthday: this.formatBirthday(this.formItem.birthday),
|
|
||||||
face: this.formItem.face,
|
|
||||||
nickName: this.formItem.nickName,
|
|
||||||
sex: this.formItem.sex
|
|
||||||
}
|
|
||||||
editMemberInfo(params).then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
Message.success('修改个人资料成功')
|
|
||||||
storage.setItem('userInfo', res.result)
|
|
||||||
this.$router.go(0)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
handleSuccess (res, file) { // 上传成功
|
})
|
||||||
this.formItem['face'] = res.result
|
}
|
||||||
},
|
function handleSuccess (res, file) { // 上传成功
|
||||||
}
|
formItem.value['face'] = res.result
|
||||||
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -74,119 +74,125 @@
|
|||||||
</UserCenterLayout>
|
</UserCenterLayout>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
|
import { getCurrentInstance, onMounted, reactive, ref } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { Message, Notice } from "@/utils/message";
|
import { Message, Notice } from "@/utils/message";
|
||||||
import { Camera, Delete, View } from '@element-plus/icons-vue';
|
import { Camera, Delete, View } from '@element-plus/icons-vue';
|
||||||
import { orderDetail } from '@/api/order.js';
|
import { orderDetail } from '@/api/order.js';
|
||||||
import { addEvaluation } from '@/api/member.js';
|
import { addEvaluation } from '@/api/member.js';
|
||||||
import { buyerUrl } from '@/plugins/request.js';
|
import { buyerUrl } from '@/plugins/request.js';
|
||||||
import storage from '@/plugins/storage';
|
import storage from '@/plugins/storage';
|
||||||
export default {
|
|
||||||
components: { Camera, Delete, View },
|
const route = useRoute();
|
||||||
data () {
|
const router = useRouter();
|
||||||
return {
|
const instance = getCurrentInstance();
|
||||||
order: {}, // 订单详情
|
|
||||||
orderGoods: {}, // 订单商品
|
const order = ref({}); // 订单详情
|
||||||
form: { // 评分展示
|
const orderGoods = ref({}); // 订单商品
|
||||||
deliveryScore: 5,
|
const form = reactive({ // 评分展示
|
||||||
serviceScore: 5,
|
deliveryScore: 5,
|
||||||
descriptionScore: 5
|
serviceScore: 5,
|
||||||
}, // 表单
|
descriptionScore: 5
|
||||||
visible: false, // 图片预览
|
}); // 表单
|
||||||
action: buyerUrl + '/buyer/common/upload/file', // 上传地址
|
const visible = ref(false); // 图片预览
|
||||||
accessToken: {}, // 验证token
|
const action = buyerUrl + '/buyer/common/upload/file'; // 上传地址
|
||||||
previewImage: '', // 预览图片地址
|
const accessToken = reactive({}); // 验证token
|
||||||
loading: false, // 提交加载状态
|
const previewImage = ref(''); // 预览图片地址
|
||||||
// el-rate SVG 通过 colors 写入 fill-color,跟随平台主题色
|
const loading = ref(false); // 提交加载状态
|
||||||
rateThemeColors: ['var(--theme-color)', 'var(--theme-color)', 'var(--theme-color)']
|
// el-rate SVG 通过 colors 写入 fill-color,跟随平台主题色
|
||||||
|
const rateThemeColors = ['var(--theme-color)', 'var(--theme-color)', 'var(--theme-color)'];
|
||||||
|
|
||||||
|
function getOrderDetail () { // 获取订单详情
|
||||||
|
orderDetail(route.query.sn).then(res => {
|
||||||
|
order.value = res.result
|
||||||
|
orderGoods.value = res.result.orderItems[route.query.index]
|
||||||
|
orderGoods.value['grade'] = 'GOOD'
|
||||||
|
orderGoods.value.uploadList = []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function save () { // 保存评价
|
||||||
|
if (!form.serviceScore || !form.deliveryScore) {
|
||||||
|
Message.warning('物流服务评价不能为空')
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!form.descriptionScore) {
|
||||||
|
Message.warning('描述评价不能为空')
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!orderGoods.value.content || !String(orderGoods.value.content).trim()) {
|
||||||
|
Message.warning('评论内容不能为空')
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
loading.value = true;
|
||||||
|
let goods = orderGoods.value
|
||||||
|
let params = {
|
||||||
|
goodsId: goods.goodsId,
|
||||||
|
orderItemSn: goods.sn,
|
||||||
|
skuId: goods.skuId,
|
||||||
|
descriptionScore: form.descriptionScore,
|
||||||
|
serviceScore: form.serviceScore,
|
||||||
|
deliveryScore: form.deliveryScore,
|
||||||
|
grade: goods.grade,
|
||||||
|
content: goods.content.trim(),
|
||||||
|
images: goods.uploadList.length ? goods.uploadList.join(',') : ''
|
||||||
|
}
|
||||||
|
addEvaluation(params).then(res => {
|
||||||
|
loading.value = false
|
||||||
|
if (res.success) {
|
||||||
|
Message.success('评价成功')
|
||||||
|
router.push('/home/CommentList')
|
||||||
|
} else {
|
||||||
|
Message.error(res.message || '评价失败,请稍后重试')
|
||||||
}
|
}
|
||||||
},
|
}).catch((err) => {
|
||||||
methods: {
|
loading.value = false;
|
||||||
getOrderDetail () { // 获取订单详情
|
Message.error(err?.message || '评价失败,请稍后重试')
|
||||||
orderDetail(this.$route.query.sn).then(res => {
|
})
|
||||||
this.order = res.result
|
}
|
||||||
this.orderGoods = res.result.orderItems[this.$route.query.index]
|
|
||||||
this.orderGoods['grade'] = 'GOOD'
|
|
||||||
this.orderGoods.uploadList = []
|
|
||||||
})
|
|
||||||
},
|
|
||||||
save () { // 保存评价
|
|
||||||
if (!this.form.serviceScore || !this.form.deliveryScore) {
|
|
||||||
Message.warning('物流服务评价不能为空')
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.form.descriptionScore) {
|
function goGoodsDetail (skuId, goodsId) { // 跳转商品详情
|
||||||
Message.warning('描述评价不能为空')
|
let routerUrl = router.resolve({
|
||||||
return false;
|
path: '/goodsDetail',
|
||||||
}
|
query: {skuId, goodsId}
|
||||||
|
})
|
||||||
|
window.open(routerUrl.href, '_blank')
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.orderGoods.content || !String(this.orderGoods.content).trim()) {
|
function handleView (name) { // 预览图片
|
||||||
Message.warning('评论内容不能为空')
|
previewImage.value = name;
|
||||||
return false;
|
visible.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loading = true;
|
function handleRemove (index) { // 移除图片
|
||||||
let goods = this.orderGoods
|
orderGoods.value.uploadList.splice(index, 1)
|
||||||
let params = {
|
instance.proxy.$forceUpdate()
|
||||||
goodsId: goods.goodsId,
|
}
|
||||||
orderItemSn: goods.sn,
|
|
||||||
skuId: goods.skuId,
|
function handleSuccess (res, file) { // 上传成功回调
|
||||||
descriptionScore: this.form.descriptionScore,
|
orderGoods.value.uploadList.push(res.result)
|
||||||
serviceScore: this.form.serviceScore,
|
instance.proxy.$forceUpdate()
|
||||||
deliveryScore: this.form.deliveryScore,
|
}
|
||||||
grade: goods.grade,
|
|
||||||
content: goods.content.trim(),
|
function handleBeforeUpload () { // 上传之前钩子
|
||||||
images: goods.uploadList.length ? goods.uploadList.join(',') : ''
|
const check = orderGoods.value.uploadList.length < 9;
|
||||||
}
|
if (!check) {
|
||||||
addEvaluation(params).then(res => {
|
Notice.warning({
|
||||||
this.loading = false
|
title: '最多可以上传九张图片'
|
||||||
if (res.success) {
|
});
|
||||||
Message.success('评价成功')
|
return check;
|
||||||
this.$router.push('/home/CommentList')
|
|
||||||
} else {
|
|
||||||
Message.error(res.message || '评价失败,请稍后重试')
|
|
||||||
}
|
|
||||||
}).catch((err) => {
|
|
||||||
this.loading = false;
|
|
||||||
Message.error(err?.message || '评价失败,请稍后重试')
|
|
||||||
})
|
|
||||||
},
|
|
||||||
goGoodsDetail (skuId, goodsId) { // 跳转商品详情
|
|
||||||
let routerUrl = this.$router.resolve({
|
|
||||||
path: '/goodsDetail',
|
|
||||||
query: {skuId, goodsId}
|
|
||||||
})
|
|
||||||
window.open(routerUrl.href, '_blank')
|
|
||||||
},
|
|
||||||
handleView (name) { // 预览图片
|
|
||||||
this.previewImage = name;
|
|
||||||
this.visible = true;
|
|
||||||
},
|
|
||||||
handleRemove (index) { // 移除图片
|
|
||||||
this.orderGoods.uploadList.splice(index, 1)
|
|
||||||
this.$forceUpdate()
|
|
||||||
},
|
|
||||||
handleSuccess (res, file) { // 上传成功回调
|
|
||||||
this.orderGoods.uploadList.push(res.result)
|
|
||||||
this.$forceUpdate()
|
|
||||||
},
|
|
||||||
handleBeforeUpload () { // 上传之前钩子
|
|
||||||
const check = this.orderGoods.uploadList.length < 9;
|
|
||||||
if (!check) {
|
|
||||||
Notice.warning({
|
|
||||||
title: '最多可以上传九张图片'
|
|
||||||
});
|
|
||||||
return check;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
window.scrollTo(0, 0)
|
|
||||||
this.accessToken.accessToken = storage.getItem('accessToken');
|
|
||||||
this.getOrderDetail()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.scrollTo(0, 0)
|
||||||
|
accessToken.accessToken = storage.getItem('accessToken');
|
||||||
|
getOrderDetail()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.delivery-rate {
|
.delivery-rate {
|
||||||
|
|||||||
@@ -79,51 +79,50 @@
|
|||||||
</UserCenterLayout>
|
</UserCenterLayout>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
|
import { computed, onMounted, ref } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { evaluationDetail } from "@/api/member.js";
|
import { evaluationDetail } from "@/api/member.js";
|
||||||
export default {
|
|
||||||
data() {
|
const route = useRoute();
|
||||||
return {
|
const router = useRouter();
|
||||||
order: {}, // 订单详情
|
|
||||||
orderGoods: {}, // 订单商品
|
const order = ref({}); // 订单详情
|
||||||
visible: false, // 图片预览
|
const orderGoods = ref({}); // 订单商品
|
||||||
previewImage: "", // 预览图片链接
|
const visible = ref(false); // 图片预览
|
||||||
loading: false, // 加载状态
|
const previewImage = ref(""); // 预览图片链接
|
||||||
// el-rate SVG 通过 colors 写入 fill-color,跟随平台主题色
|
const loading = ref(false); // 加载状态
|
||||||
rateThemeColors: ["var(--theme-color)", "var(--theme-color)", "var(--theme-color)"],
|
// el-rate SVG 通过 colors 写入 fill-color,跟随平台主题色
|
||||||
};
|
const rateThemeColors = ref(["var(--theme-color)", "var(--theme-color)", "var(--theme-color)"]);
|
||||||
},
|
|
||||||
computed: {
|
const gradeText = computed(() => {
|
||||||
gradeText () {
|
const map = { GOOD: '好评', MODERATE: '中评', WORSE: '差评' }
|
||||||
const map = { GOOD: '好评', MODERATE: '中评', WORSE: '差评' }
|
return map[orderGoods.value.grade] || '评价详情'
|
||||||
return map[this.orderGoods.grade] || '评价详情'
|
});
|
||||||
}
|
|
||||||
},
|
function getDetail() {
|
||||||
methods: {
|
// 获取评价详情
|
||||||
getDetail() {
|
evaluationDetail(route.query.id).then((res) => {
|
||||||
// 获取评价详情
|
if (res.success) orderGoods.value = res.result;
|
||||||
evaluationDetail(this.$route.query.id).then((res) => {
|
});
|
||||||
if (res.success) this.orderGoods = res.result;
|
}
|
||||||
});
|
function goGoodsDetail(skuId, goodsId) {
|
||||||
},
|
// 跳转商品详情
|
||||||
goGoodsDetail(skuId, goodsId) {
|
let routerUrl = router.resolve({
|
||||||
// 跳转商品详情
|
path: "/goodsDetail",
|
||||||
let routerUrl = this.$router.resolve({
|
query: { skuId, goodsId },
|
||||||
path: "/goodsDetail",
|
});
|
||||||
query: { skuId, goodsId },
|
window.open(routerUrl.href, "_blank");
|
||||||
});
|
}
|
||||||
window.open(routerUrl.href, "_blank");
|
function handleView(name) {
|
||||||
},
|
// 预览图片
|
||||||
handleView(name) {
|
previewImage.value = name;
|
||||||
// 预览图片
|
visible.value = true;
|
||||||
this.previewImage = name;
|
}
|
||||||
this.visible = true;
|
|
||||||
},
|
onMounted(() => {
|
||||||
},
|
getDetail();
|
||||||
mounted() {
|
});
|
||||||
this.getDetail();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.add-eval {
|
.add-eval {
|
||||||
|
|||||||
@@ -22,86 +22,90 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { Message, Modal } from "@/utils/message";
|
import { Message, Modal } from "@/utils/message";
|
||||||
import { delMemberMsg, getMemberMessage, readMemberMsg } from '@/api/member.js';
|
import { delMemberMsg, getMemberMessage, readMemberMsg } from '@/api/member.js';
|
||||||
import { refreshUnreadMessage } from '@/utils/unreadMessage';
|
import { refreshUnreadMessage } from '@/utils/unreadMessage';
|
||||||
|
|
||||||
|
defineOptions({ name: 'MsgDetail' });
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
const CACHE_KEY = 'buyer-member-message';
|
const CACHE_KEY = 'buyer-member-message';
|
||||||
|
|
||||||
export default {
|
const detail = ref({});
|
||||||
name: 'MsgDetail',
|
const loaded = ref(false);
|
||||||
data () {
|
|
||||||
return {
|
function loadDetail () {
|
||||||
detail: {},
|
const id = route.query.id
|
||||||
loaded: false
|
if (!id) {
|
||||||
}
|
loaded.value = true
|
||||||
},
|
return
|
||||||
mounted () {
|
}
|
||||||
this.loadDetail()
|
const cached = readCache(id)
|
||||||
},
|
if (cached) detail.value = cached
|
||||||
methods: {
|
getMemberMessage(id).then(res => {
|
||||||
loadDetail () {
|
if (res && res.success && res.result && res.result.id) {
|
||||||
const id = this.$route.query.id
|
detail.value = res.result
|
||||||
if (!id) {
|
sessionStorage.setItem(CACHE_KEY, JSON.stringify(res.result))
|
||||||
this.loaded = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const cached = this.readCache(id)
|
|
||||||
if (cached) this.detail = cached
|
|
||||||
getMemberMessage(id).then(res => {
|
|
||||||
if (res && res.success && res.result && res.result.id) {
|
|
||||||
this.detail = res.result
|
|
||||||
sessionStorage.setItem(CACHE_KEY, JSON.stringify(res.result))
|
|
||||||
}
|
|
||||||
}).catch((err) => {
|
|
||||||
console.warn('获取消息详情失败', err)
|
|
||||||
}).finally(() => {
|
|
||||||
this.loaded = true
|
|
||||||
this.markRead()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
readCache (id) {
|
|
||||||
try {
|
|
||||||
const raw = sessionStorage.getItem(CACHE_KEY)
|
|
||||||
if (!raw) return null
|
|
||||||
const message = JSON.parse(raw)
|
|
||||||
return message && String(message.id) === String(id) ? message : null
|
|
||||||
} catch (e) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
markRead () {
|
|
||||||
if (!this.detail.id || this.detail.status !== 'UN_READY') return
|
|
||||||
readMemberMsg(this.detail.id).then(res => {
|
|
||||||
if (res && res.success) {
|
|
||||||
this.detail.status = 'ALREADY_READY'
|
|
||||||
refreshUnreadMessage()
|
|
||||||
}
|
|
||||||
}).catch(() => {
|
|
||||||
// 标记已读为尽力而为操作,失败可忽略
|
|
||||||
})
|
|
||||||
},
|
|
||||||
goBack () {
|
|
||||||
this.$router.push({ name: 'MsgList' })
|
|
||||||
},
|
|
||||||
removeMessage () {
|
|
||||||
Modal.confirm({
|
|
||||||
title: '确认删除',
|
|
||||||
content: '确认要删除此消息?',
|
|
||||||
loading: true,
|
|
||||||
onOk: () =>
|
|
||||||
delMemberMsg(this.detail.id).then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
Message.success('消息已成功放入回收站')
|
|
||||||
refreshUnreadMessage()
|
|
||||||
this.goBack()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
console.warn('获取消息详情失败', err)
|
||||||
|
}).finally(() => {
|
||||||
|
loaded.value = true
|
||||||
|
markRead()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function readCache (id) {
|
||||||
|
try {
|
||||||
|
const raw = sessionStorage.getItem(CACHE_KEY)
|
||||||
|
if (!raw) return null
|
||||||
|
const message = JSON.parse(raw)
|
||||||
|
return message && String(message.id) === String(id) ? message : null
|
||||||
|
} catch (e) {
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function markRead () {
|
||||||
|
if (!detail.value.id || detail.value.status !== 'UN_READY') return
|
||||||
|
readMemberMsg(detail.value.id).then(res => {
|
||||||
|
if (res && res.success) {
|
||||||
|
detail.value.status = 'ALREADY_READY'
|
||||||
|
refreshUnreadMessage()
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
// 标记已读为尽力而为操作,失败可忽略
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function goBack () {
|
||||||
|
router.push({ name: 'MsgList' })
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeMessage () {
|
||||||
|
Modal.confirm({
|
||||||
|
title: '确认删除',
|
||||||
|
content: '确认要删除此消息?',
|
||||||
|
loading: true,
|
||||||
|
onOk: () =>
|
||||||
|
delMemberMsg(detail.value.id).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
Message.success('消息已成功放入回收站')
|
||||||
|
refreshUnreadMessage()
|
||||||
|
goBack()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadDetail()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -47,85 +47,85 @@
|
|||||||
</UserCenterLayout>
|
</UserCenterLayout>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
|
import { computed, onMounted, reactive, ref } from "vue";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
import { Message, Modal } from "@/utils/message";
|
import { Message, Modal } from "@/utils/message";
|
||||||
import {memberMsgList, readMemberMsg, delMemberMsg} from '@/api/member.js'
|
import {memberMsgList, readMemberMsg, delMemberMsg} from '@/api/member.js'
|
||||||
export default {
|
import { useAppStore } from "@/stores/app";
|
||||||
data() {
|
|
||||||
return {
|
const router = useRouter();
|
||||||
messageData: {},
|
const appStore = useAppStore();
|
||||||
status: ['未读', '已读', '回收站'],
|
|
||||||
params: {
|
const messageData = ref({});
|
||||||
pageNumber: 1,
|
const status = ref(['未读', '已读', '回收站']);
|
||||||
pageSize: 20,
|
const params = reactive({
|
||||||
status: 'UN_READY'
|
pageNumber: 1,
|
||||||
},
|
pageSize: 20,
|
||||||
}
|
status: 'UN_READY'
|
||||||
},
|
});
|
||||||
computed: {
|
|
||||||
activeStatusIndex () {
|
const activeStatusIndex = computed(() => {
|
||||||
const map = ['UN_READY', 'ALREADY_READY', 'ALREADY_REMOVE'];
|
const map = ['UN_READY', 'ALREADY_READY', 'ALREADY_REMOVE'];
|
||||||
const index = map.indexOf(this.params.status);
|
const index = map.indexOf(params.status);
|
||||||
return index > -1 ? index : 0;
|
return index > -1 ? index : 0;
|
||||||
}
|
});
|
||||||
},
|
|
||||||
methods: {
|
function statusChange (index) {
|
||||||
statusChange (index) {
|
if (index === 0) { params.status = 'UN_READY' }
|
||||||
if (index === 0) { this.params.status = 'UN_READY' }
|
if (index === 1) { params.status = 'ALREADY_READY' }
|
||||||
if (index === 1) { this.params.status = 'ALREADY_READY' }
|
if (index === 2) { params.status = 'ALREADY_REMOVE' }
|
||||||
if (index === 2) { this.params.status = 'ALREADY_REMOVE' }
|
params.pageNumber = 1
|
||||||
this.params.pageNumber = 1
|
getList()
|
||||||
this.getList()
|
|
||||||
},
|
|
||||||
changePage (v) {
|
|
||||||
this.params.pageNumber = v;
|
|
||||||
this.getList();
|
|
||||||
},
|
|
||||||
changePageSize (v) {
|
|
||||||
this.params.pageNumber = 1
|
|
||||||
this.params.pageSize = v;
|
|
||||||
this.getList();
|
|
||||||
},
|
|
||||||
openDetail (row) {
|
|
||||||
sessionStorage.setItem('buyer-member-message', JSON.stringify(row))
|
|
||||||
this.$router.push({ name: 'MsgDetail', query: { id: row.id } })
|
|
||||||
},
|
|
||||||
getList () {
|
|
||||||
memberMsgList(this.params).then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
this.messageData = res.result;
|
|
||||||
if (this.params.status === 'UN_READY') {
|
|
||||||
this.$store.commit('SET_UNREAD_MSG', Number(res.result.total) || 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
setRead (id) {
|
|
||||||
readMemberMsg(id).then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
this.getList()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
removeMessage (id) {
|
|
||||||
Modal.confirm({
|
|
||||||
title: '确认删除',
|
|
||||||
content: '确认要删除此消息?',
|
|
||||||
loading: true,
|
|
||||||
onOk: () =>
|
|
||||||
delMemberMsg(id).then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
Message.success('消息已成功放入回收站');
|
|
||||||
this.getList();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
this.getList()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
function changePage (v) {
|
||||||
|
params.pageNumber = v;
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
function changePageSize (v) {
|
||||||
|
params.pageNumber = 1
|
||||||
|
params.pageSize = v;
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
function openDetail (row) {
|
||||||
|
sessionStorage.setItem('buyer-member-message', JSON.stringify(row))
|
||||||
|
router.push({ name: 'MsgDetail', query: { id: row.id } })
|
||||||
|
}
|
||||||
|
function getList () {
|
||||||
|
memberMsgList(params).then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
messageData.value = res.result;
|
||||||
|
if (params.status === 'UN_READY') {
|
||||||
|
appStore.setUnreadMsg(Number(res.result.total) || 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function setRead (id) {
|
||||||
|
readMemberMsg(id).then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function removeMessage (id) {
|
||||||
|
Modal.confirm({
|
||||||
|
title: '确认删除',
|
||||||
|
content: '确认要删除此消息?',
|
||||||
|
loading: true,
|
||||||
|
onOk: () =>
|
||||||
|
delMemberMsg(id).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
Message.success('消息已成功放入回收站');
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getList()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.msg-table {
|
.msg-table {
|
||||||
|
|||||||
@@ -2,14 +2,14 @@
|
|||||||
<div>
|
<div>
|
||||||
<UserCenterLayout>
|
<UserCenterLayout>
|
||||||
<div class="add-box">
|
<div class="add-box">
|
||||||
<el-form :model="formData" ref="form" label-position="left" label-width="100px" :rules="ruleInline">
|
<el-form :model="formData" ref="formRef" label-position="left" label-width="100px" :rules="ruleInline">
|
||||||
<el-form-item label="收件人" prop="name">
|
<el-form-item label="收件人" prop="name">
|
||||||
<el-input v-model="formData.name" placeholder="请输入收件人姓名" style="width: 600px"></el-input>
|
<el-input v-model="formData.name" placeholder="请输入收件人姓名" style="width: 600px"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="收件地区" prop="address">
|
<el-form-item label="收件地区" prop="address">
|
||||||
<span>{{ formData.address || '暂无地址' }}</span>
|
<span>{{ formData.address || '暂无地址' }}</span>
|
||||||
|
|
||||||
<el-button type="default" style="margin-left: 10px;" size="small" @click="$refs.map.open()">选择</el-button>
|
<el-button type="default" style="margin-left: 10px;" size="small" @click="mapRef.open()">选择</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="详细地址" prop="detail">
|
<el-form-item label="详细地址" prop="detail">
|
||||||
<el-input v-model="formData.detail" placeholder="请输入详细地址" style="width: 600px"></el-input>
|
<el-input v-model="formData.detail" placeholder="请输入详细地址" style="width: 600px"></el-input>
|
||||||
@@ -32,11 +32,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</UserCenterLayout>
|
</UserCenterLayout>
|
||||||
|
|
||||||
<multipleMap ref="map" @callback="getAddress" />
|
<multipleMap ref="mapRef" @callback="getAddress" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { onMounted, ref } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import multipleMap from "@/components/map/multiple-map";
|
import multipleMap from "@/components/map/multiple-map";
|
||||||
import * as RegExp from "@/plugins/RegExp.js";
|
import * as RegExp from "@/plugins/RegExp.js";
|
||||||
import {
|
import {
|
||||||
@@ -44,108 +46,107 @@ import {
|
|||||||
editMemberAddress,
|
editMemberAddress,
|
||||||
getAddrDetail,
|
getAddrDetail,
|
||||||
} from "@/api/address";
|
} from "@/api/address";
|
||||||
export default {
|
|
||||||
name: "AddAddress",
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
formData: {
|
|
||||||
// 添加地址表单
|
|
||||||
isDefault: false,
|
|
||||||
},
|
|
||||||
ruleInline: {
|
|
||||||
// 验证规则
|
|
||||||
name: [{ required: true, message: "请输入姓名", trigger: "blur" }],
|
|
||||||
address: [{ required: true, message: "请输入地址", trigger: "change" }],
|
|
||||||
detail: [
|
|
||||||
{ required: true, message: "请输入详细地址", trigger: "blur" },
|
|
||||||
],
|
|
||||||
mobile: [
|
|
||||||
{ required: true, message: "请输入手机号码" },
|
|
||||||
{
|
|
||||||
pattern: RegExp.mobile,
|
|
||||||
trigger: "blur",
|
|
||||||
message: "请输入正确的手机号",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
loading: false, // 加载状态
|
|
||||||
mapMsg: {}, // 地图信息
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
save() {
|
|
||||||
// 保存地址
|
|
||||||
this.$refs.form.validate((valid) => {
|
|
||||||
if (valid) {
|
|
||||||
const params = JSON.parse(JSON.stringify(this.formData));
|
|
||||||
params.consigneeAddressPath = params.address.replace(/\s/g, ",");
|
|
||||||
delete params.address;
|
|
||||||
this.loading = true;
|
|
||||||
if (this.$route.query.id) {
|
|
||||||
editMemberAddress(params)
|
|
||||||
.then((res) => {
|
|
||||||
this.loading = false;
|
|
||||||
if (res.success) {
|
|
||||||
this.$router.push("/home/MyAddress");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
this.loading = false;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
newMemberAddress(params)
|
|
||||||
.then((res) => {
|
|
||||||
this.loading = false;
|
|
||||||
if (res.success) {
|
|
||||||
this.$router.push("/home/MyAddress");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
this.loading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getAddrById(id) {
|
|
||||||
// 获取地址详情
|
|
||||||
getAddrDetail(id).then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
const data = res.result;
|
|
||||||
data.address = res.result.consigneeAddressPath.replace(/,/g, " ");
|
|
||||||
this.formData = data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getAddress(val) {
|
|
||||||
// 获取地图选择信息
|
|
||||||
if(val.type === 'select'){
|
|
||||||
const paths = val.data.map(item => item.name).join(',')
|
|
||||||
const ids = val.data.map(item => item.id).join(',')
|
|
||||||
this.formData['address'] = paths
|
|
||||||
this.formData['consigneeAddressIdPath'] = ids
|
|
||||||
const coord = val.data[val.data.length - 1].center.split(',')
|
|
||||||
this.formData.lat = coord[1]
|
|
||||||
this.formData.lon = coord[0]
|
|
||||||
}else{
|
|
||||||
this.formData["address"] = val.data.addr;
|
|
||||||
this.formData["consigneeAddressIdPath"] = val.data.addrId;
|
|
||||||
this.formData["detail"] = val.data.address;
|
|
||||||
this.formData.lat = val.data.position.lat;
|
|
||||||
this.formData.lon = val.data.position.lng;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
defineOptions({ name: "AddAddress" });
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const formRef = ref(null);
|
||||||
|
const mapRef = ref(null);
|
||||||
|
|
||||||
|
const formData = ref({
|
||||||
|
// 添加地址表单
|
||||||
|
isDefault: false,
|
||||||
|
});
|
||||||
|
const ruleInline = {
|
||||||
|
// 验证规则
|
||||||
|
name: [{ required: true, message: "请输入姓名", trigger: "blur" }],
|
||||||
|
address: [{ required: true, message: "请输入地址", trigger: "change" }],
|
||||||
|
detail: [
|
||||||
|
{ required: true, message: "请输入详细地址", trigger: "blur" },
|
||||||
|
],
|
||||||
|
mobile: [
|
||||||
|
{ required: true, message: "请输入手机号码" },
|
||||||
|
{
|
||||||
|
pattern: RegExp.mobile,
|
||||||
|
trigger: "blur",
|
||||||
|
message: "请输入正确的手机号",
|
||||||
},
|
},
|
||||||
},
|
],
|
||||||
mounted() {
|
|
||||||
const id = this.$route.query.id;
|
|
||||||
if (id) this.getAddrById(id);
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
multipleMap
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
const loading = ref(false); // 加载状态
|
||||||
|
const mapMsg = ref({}); // 地图信息
|
||||||
|
|
||||||
|
// 保存地址
|
||||||
|
function save() {
|
||||||
|
formRef.value.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
const params = JSON.parse(JSON.stringify(formData.value));
|
||||||
|
params.consigneeAddressPath = params.address.replace(/\s/g, ",");
|
||||||
|
delete params.address;
|
||||||
|
loading.value = true;
|
||||||
|
if (route.query.id) {
|
||||||
|
editMemberAddress(params)
|
||||||
|
.then((res) => {
|
||||||
|
loading.value = false;
|
||||||
|
if (res.success) {
|
||||||
|
router.push("/home/MyAddress");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
newMemberAddress(params)
|
||||||
|
.then((res) => {
|
||||||
|
loading.value = false;
|
||||||
|
if (res.success) {
|
||||||
|
router.push("/home/MyAddress");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function getAddrById(id) {
|
||||||
|
// 获取地址详情
|
||||||
|
getAddrDetail(id).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
const data = res.result;
|
||||||
|
data.address = res.result.consigneeAddressPath.replace(/,/g, " ");
|
||||||
|
formData.value = data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function getAddress(val) {
|
||||||
|
// 获取地图选择信息
|
||||||
|
if(val.type === 'select'){
|
||||||
|
const paths = val.data.map(item => item.name).join(',')
|
||||||
|
const ids = val.data.map(item => item.id).join(',')
|
||||||
|
formData.value['address'] = paths
|
||||||
|
formData.value['consigneeAddressIdPath'] = ids
|
||||||
|
const coord = val.data[val.data.length - 1].center.split(',')
|
||||||
|
formData.value.lat = coord[1]
|
||||||
|
formData.value.lon = coord[0]
|
||||||
|
}else{
|
||||||
|
formData.value["address"] = val.data.addr;
|
||||||
|
formData.value["consigneeAddressIdPath"] = val.data.addrId;
|
||||||
|
formData.value["detail"] = val.data.address;
|
||||||
|
formData.value.lat = val.data.position.lat;
|
||||||
|
formData.value.lon = val.data.position.lng;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const id = route.query.id;
|
||||||
|
if (id) getAddrById(id);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -102,7 +102,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<el-form ref="form" :model="form" label-position="left" label-width="100px" :rules="rules">
|
<el-form ref="formRef" :model="form" label-position="left" label-width="100px" :rules="rules">
|
||||||
<el-form-item label="物流公司" prop="logisticsId">
|
<el-form-item label="物流公司" prop="logisticsId">
|
||||||
<el-select v-model="form.logisticsId" placeholder="请选择物流公司">
|
<el-select v-model="form.logisticsId" placeholder="请选择物流公司">
|
||||||
<el-option v-for="item in companyList" :value="item.id" :key="item.id">{{ item.name }}</el-option>
|
<el-option v-for="item in companyList" :value="item.id" :key="item.id">{{ item.name }}</el-option>
|
||||||
@@ -131,170 +131,169 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { nextTick, onMounted, reactive, ref } from "vue";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
import { Message, Modal } from "@/utils/message";
|
import { Message, Modal } from "@/utils/message";
|
||||||
import {afterSaleList, cancelAfterSale} from '@/api/member.js';
|
import {afterSaleList, cancelAfterSale} from '@/api/member.js';
|
||||||
import {afterSaleDelivery, getLogisticsCompany} from '@/api/order.js';
|
import {afterSaleDelivery, getLogisticsCompany} from '@/api/order.js';
|
||||||
import {afterSaleStatusList} from '../enumeration.js'
|
import {afterSaleStatusList} from '../enumeration.js'
|
||||||
|
|
||||||
export default {
|
defineOptions({ name: 'AfterSale' });
|
||||||
name: 'AfterSale',
|
|
||||||
data() {
|
const router = useRouter();
|
||||||
return {
|
|
||||||
orderList: [], // 订单列表
|
const formRef = ref(null);
|
||||||
params: { // 请求参数
|
|
||||||
pageNumber: 1,
|
const orderList = ref([]); // 订单列表
|
||||||
pageSize: 20,
|
const params = reactive({ // 请求参数
|
||||||
keywords: '',
|
pageNumber: 1,
|
||||||
sn: '',
|
pageSize: 20,
|
||||||
sort: 'createTime',
|
keywords: '',
|
||||||
order: 'desc'
|
sn: '',
|
||||||
},
|
sort: 'createTime',
|
||||||
// 状态数组
|
order: 'desc'
|
||||||
afterSaleStatusList,
|
});
|
||||||
total: 0, // 订单总数
|
const total = ref(0); // 订单总数
|
||||||
spinShow: false, // 加载状态
|
const spinShow = ref(false); // 加载状态
|
||||||
companyList: [], // 物流公司列表
|
const companyList = ref([]); // 物流公司列表
|
||||||
logisticsShow: false, // 物流信息modal
|
const logisticsShow = ref(false); // 物流信息modal
|
||||||
singleOrder: {}, // 单独的售后信息
|
const singleOrder = ref({}); // 单独的售后信息
|
||||||
form: { // 物流信息数据
|
const form = reactive({ // 物流信息数据
|
||||||
afterSaleSn: '',
|
afterSaleSn: '',
|
||||||
logisticsId: '',
|
logisticsId: '',
|
||||||
logisticsNo: '',
|
logisticsNo: '',
|
||||||
mDeliverTime: ''
|
mDeliverTime: ''
|
||||||
},
|
});
|
||||||
rules: { // 必填校验
|
const rules = { // 必填校验
|
||||||
logisticsId: [{required: true, message: '请选择物流公司'}],
|
logisticsId: [{required: true, message: '请选择物流公司'}],
|
||||||
logisticsNo: [{required: true, message: '请填写物流编号'}],
|
logisticsNo: [{required: true, message: '请填写物流编号'}],
|
||||||
mDeliverTime: [{required: true, message: '请选择发货时间'}]
|
mDeliverTime: [{required: true, message: '请选择发货时间'}]
|
||||||
},
|
|
||||||
submitLoading: false, // 提交加载状态
|
|
||||||
changeWay: [
|
|
||||||
{ name: '全部', status: '' },
|
|
||||||
{ name: '申请中', status: 'APPLY' },
|
|
||||||
{ name: '通过', status: 'PASS' },
|
|
||||||
{ name: '完成售后', status: 'COMPLETE' },
|
|
||||||
],
|
|
||||||
activeTab: 0,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.getList();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
goDetail(sn) { // 跳转售后详情
|
|
||||||
this.$router.push({
|
|
||||||
name: 'AfterSaleDetail',
|
|
||||||
query: {sn}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
cancel(sn) { // 取消售后申请
|
|
||||||
Modal.confirm({
|
|
||||||
title: '取消',
|
|
||||||
content: '确定取消此次售后申请吗?',
|
|
||||||
onOk: () => {
|
|
||||||
cancelAfterSale(sn).then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
Message.success('取消售后申请成功')
|
|
||||||
this.getList()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
onCancel: () => {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
goodsDetail(skuId, goodsId) {
|
|
||||||
// 跳转商品详情
|
|
||||||
let routeUrl = this.$router.resolve({
|
|
||||||
path: '/goodsDetail',
|
|
||||||
query: {skuId, goodsId}
|
|
||||||
});
|
|
||||||
window.open(routeUrl.href, '_blank');
|
|
||||||
},
|
|
||||||
// 跳转店铺首页
|
|
||||||
shopPage(id) {
|
|
||||||
this.$router.push({
|
|
||||||
path: '/merchant',
|
|
||||||
query: {id: id}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
change(index) {
|
|
||||||
this.activeTab = index
|
|
||||||
this.params.pageNumber = 1
|
|
||||||
const current = this.changeWay[index]
|
|
||||||
if (!current || !current.status) {
|
|
||||||
delete this.params.serviceStatus
|
|
||||||
} else {
|
|
||||||
this.params.serviceStatus = current.status
|
|
||||||
}
|
|
||||||
this.getList()
|
|
||||||
},
|
|
||||||
getList() { // 获取售后列表
|
|
||||||
this.spinShow = true;
|
|
||||||
let params = JSON.parse(JSON.stringify(this.params))
|
|
||||||
afterSaleList(params).then(res => {
|
|
||||||
this.spinShow = false
|
|
||||||
if (res.success) {
|
|
||||||
this.orderList = res.result.records;
|
|
||||||
this.total = res.result.total;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
changePageNum(val) { // 修改页码
|
|
||||||
this.params.pageNumber = val;
|
|
||||||
this.getList()
|
|
||||||
},
|
|
||||||
changePageSize(val) { // 修改页数
|
|
||||||
this.params.pageNumber = 1;
|
|
||||||
this.params.pageSize = val;
|
|
||||||
this.getList()
|
|
||||||
},
|
|
||||||
filterOrderStatus(status) { // 获取订单状态中文
|
|
||||||
const ob = this.afterSaleStatusList.filter(e => {
|
|
||||||
return e.status === status
|
|
||||||
});
|
|
||||||
return ob && ob[0] ? ob[0].name : status
|
|
||||||
},
|
|
||||||
// 获取物流公司列表
|
|
||||||
getCompany() {
|
|
||||||
getLogisticsCompany().then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
this.companyList = res.result
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// 提交物流信息
|
|
||||||
submitDelivery() {
|
|
||||||
this.$refs.form?.validate((valid) => {
|
|
||||||
if (!valid) return;
|
|
||||||
this.submitLoading = true;
|
|
||||||
afterSaleDelivery(this.form).then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
this.logisticsShow = false;
|
|
||||||
Message.success('提交成功');
|
|
||||||
this.getList();
|
|
||||||
}
|
|
||||||
this.submitLoading = false;
|
|
||||||
}).catch(() => {
|
|
||||||
this.submitLoading = false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 提交物流modal
|
|
||||||
openModal(row) {
|
|
||||||
this.singleOrder = row;
|
|
||||||
this.logisticsShow = true;
|
|
||||||
if (!this.companyList.length) {
|
|
||||||
this.getCompany();
|
|
||||||
}
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.$refs.form?.resetFields();
|
|
||||||
this.form.afterSaleSn = row.sn;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
const submitLoading = ref(false); // 提交加载状态
|
||||||
|
const changeWay = [
|
||||||
|
{ name: '全部', status: '' },
|
||||||
|
{ name: '申请中', status: 'APPLY' },
|
||||||
|
{ name: '通过', status: 'PASS' },
|
||||||
|
{ name: '完成售后', status: 'COMPLETE' },
|
||||||
|
];
|
||||||
|
const activeTab = ref(0);
|
||||||
|
|
||||||
|
function goDetail(sn) { // 跳转售后详情
|
||||||
|
router.push({
|
||||||
|
name: 'AfterSaleDetail',
|
||||||
|
query: {sn}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function cancel(sn) { // 取消售后申请
|
||||||
|
Modal.confirm({
|
||||||
|
title: '取消',
|
||||||
|
content: '确定取消此次售后申请吗?',
|
||||||
|
onOk: () => {
|
||||||
|
cancelAfterSale(sn).then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
Message.success('取消售后申请成功')
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onCancel: () => {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function goodsDetail(skuId, goodsId) {
|
||||||
|
// 跳转商品详情
|
||||||
|
let routeUrl = router.resolve({
|
||||||
|
path: '/goodsDetail',
|
||||||
|
query: {skuId, goodsId}
|
||||||
|
});
|
||||||
|
window.open(routeUrl.href, '_blank');
|
||||||
|
}
|
||||||
|
// 跳转店铺首页
|
||||||
|
function shopPage(id) {
|
||||||
|
router.push({
|
||||||
|
path: '/merchant',
|
||||||
|
query: {id: id}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function change(index) {
|
||||||
|
activeTab.value = index
|
||||||
|
params.pageNumber = 1
|
||||||
|
const current = changeWay[index]
|
||||||
|
if (!current || !current.status) {
|
||||||
|
delete params.serviceStatus
|
||||||
|
} else {
|
||||||
|
params.serviceStatus = current.status
|
||||||
|
}
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
function getList() { // 获取售后列表
|
||||||
|
spinShow.value = true;
|
||||||
|
let paramsData = JSON.parse(JSON.stringify(params))
|
||||||
|
afterSaleList(paramsData).then(res => {
|
||||||
|
spinShow.value = false
|
||||||
|
if (res.success) {
|
||||||
|
orderList.value = res.result.records;
|
||||||
|
total.value = res.result.total;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function changePageNum(val) { // 修改页码
|
||||||
|
params.pageNumber = val;
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
function changePageSize(val) { // 修改页数
|
||||||
|
params.pageNumber = 1;
|
||||||
|
params.pageSize = val;
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
function filterOrderStatus(status) { // 获取订单状态中文
|
||||||
|
const ob = afterSaleStatusList.filter(e => {
|
||||||
|
return e.status === status
|
||||||
|
});
|
||||||
|
return ob && ob[0] ? ob[0].name : status
|
||||||
|
}
|
||||||
|
// 获取物流公司列表
|
||||||
|
function getCompany() {
|
||||||
|
getLogisticsCompany().then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
companyList.value = res.result
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 提交物流信息
|
||||||
|
function submitDelivery() {
|
||||||
|
formRef.value?.validate((valid) => {
|
||||||
|
if (!valid) return;
|
||||||
|
submitLoading.value = true;
|
||||||
|
afterSaleDelivery(form).then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
logisticsShow.value = false;
|
||||||
|
Message.success('提交成功');
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
submitLoading.value = false;
|
||||||
|
}).catch(() => {
|
||||||
|
submitLoading.value = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 提交物流modal
|
||||||
|
function openModal(row) {
|
||||||
|
singleOrder.value = row;
|
||||||
|
logisticsShow.value = true;
|
||||||
|
if (!companyList.value.length) {
|
||||||
|
getCompany();
|
||||||
|
}
|
||||||
|
nextTick(() => {
|
||||||
|
formRef.value?.resetFields();
|
||||||
|
form.afterSaleSn = row.sn;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getList();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -106,7 +106,7 @@
|
|||||||
<span>提交物流信息</span>
|
<span>提交物流信息</span>
|
||||||
</p></template>
|
</p></template>
|
||||||
<div>
|
<div>
|
||||||
<el-form ref="form" :model="form" label-position="left" label-width="100px" :rules="rules">
|
<el-form ref="formRef" :model="form" label-position="left" label-width="100px" :rules="rules">
|
||||||
<el-form-item label="物流公司" prop="logisticsId">
|
<el-form-item label="物流公司" prop="logisticsId">
|
||||||
<el-select v-model="form.logisticsId" placeholder="请选择物流公司">
|
<el-select v-model="form.logisticsId" placeholder="请选择物流公司">
|
||||||
<el-option v-for="item in companyList" :value="item.id" :key="item.id">{{ item.name }}</el-option>
|
<el-option v-for="item in companyList" :value="item.id" :key="item.id">{{ item.name }}</el-option>
|
||||||
@@ -135,150 +135,151 @@
|
|||||||
</UserCenterLayout>
|
</UserCenterLayout>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
|
import { computed, nextTick, onMounted, reactive, ref } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { Message, Modal } from "@/utils/message";
|
import { Message, Modal } from "@/utils/message";
|
||||||
import { afterSaleDetail, afterSaleLog, afterSaleReason, cancelAfterSale } from '@/api/member.js';
|
import { afterSaleDetail, afterSaleLog, afterSaleReason, cancelAfterSale } from '@/api/member.js';
|
||||||
import { afterSaleDelivery, getLogisticsCompany } from '@/api/order.js';
|
import { afterSaleDelivery, getLogisticsCompany } from '@/api/order.js';
|
||||||
import { afterSaleStatusList } from '../enumeration.js'
|
import { afterSaleStatusList } from '../enumeration.js'
|
||||||
export default {
|
|
||||||
name: 'aftersale-detail',
|
defineOptions({ name: 'aftersale-detail' });
|
||||||
data () {
|
|
||||||
return {
|
const route = useRoute();
|
||||||
afterSale: {}, // 售后详情数据
|
const router = useRouter();
|
||||||
logList: [], // 日志
|
|
||||||
reasonList: [], // 售后原因列表
|
const afterSale = ref({}); // 售后详情数据
|
||||||
afterSaleStatusList, // 售后状态列表
|
const logList = ref([]); // 日志
|
||||||
companyList: [], // 物流公司列表
|
const reasonList = ref([]); // 售后原因列表
|
||||||
logisticsShow: false, // 物流信息modal
|
const companyList = ref([]); // 物流公司列表
|
||||||
form: { // 物流信息数据
|
const logisticsShow = ref(false); // 物流信息modal
|
||||||
afterSaleSn: '',
|
const form = reactive({ // 物流信息数据
|
||||||
logisticsId: '',
|
afterSaleSn: '',
|
||||||
logisticsNo: '',
|
logisticsId: '',
|
||||||
mDeliverTime: ''
|
logisticsNo: '',
|
||||||
},
|
mDeliverTime: ''
|
||||||
rules: { // 必填校验
|
});
|
||||||
logisticsId: [{ required: true, message: '请选择物流公司' }],
|
const rules = { // 必填校验
|
||||||
logisticsNo: [{ required: true, message: '请填写物流编号' }],
|
logisticsId: [{ required: true, message: '请选择物流公司' }],
|
||||||
mDeliverTime: [{ required: true, message: '请选择发货时间' }]
|
logisticsNo: [{ required: true, message: '请填写物流编号' }],
|
||||||
},
|
mDeliverTime: [{ required: true, message: '请选择发货时间' }]
|
||||||
submitLoading: false // 提交加载状态
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
canSubmitLogistics () {
|
|
||||||
return this.afterSale.serviceStatus == 'PASS' && this.afterSale.serviceType != 'RETURN_MONEY'
|
|
||||||
},
|
|
||||||
canCancel () {
|
|
||||||
return this.afterSale.afterSaleAllowOperationVO && this.afterSale.afterSaleAllowOperationVO.cancel
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getDetail () { // 售后详情
|
|
||||||
afterSaleDetail(this.$route.query.sn).then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
this.afterSale = res.result;
|
|
||||||
this.afterSale.serviceName = this.filterOrderStatus(this.afterSale.serviceStatus)
|
|
||||||
// 如果是原因id,则调接口查询原因中文释义
|
|
||||||
const pattern3 = new RegExp('[0-9]+');
|
|
||||||
if (pattern3.test(this.afterSale.reason)) {
|
|
||||||
this.getReason(this.afterSale.serviceType)
|
|
||||||
} else {
|
|
||||||
this.afterSale['reasonName'] = this.afterSale.reason
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
getReason (type) { // 获取售后原因
|
|
||||||
afterSaleReason(type).then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
this.reasonList = res.result
|
|
||||||
this.reasonList.forEach(e => {
|
|
||||||
if (e.id === this.afterSale.reason) {
|
|
||||||
this.afterSale['reasonName'] = e.reason
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
getLog () { // 获取售后日志
|
|
||||||
afterSaleLog(this.$route.query.sn).then(res => {
|
|
||||||
this.logList = (res.result || []).slice().sort((a, b) => {
|
|
||||||
return new Date(a.createTime).getTime() - new Date(b.createTime).getTime()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
logDesc (log) {
|
|
||||||
const operator = log.operatorName ? `操作人:${log.operatorName}` : ''
|
|
||||||
return [operator, log.createTime].filter(Boolean).join(' ')
|
|
||||||
},
|
|
||||||
shopPage (id) {
|
|
||||||
if (!id) return
|
|
||||||
this.$router.push({
|
|
||||||
path: '/merchant',
|
|
||||||
query: { id }
|
|
||||||
})
|
|
||||||
},
|
|
||||||
filterOrderStatus (status) { // 获取订单状态中文
|
|
||||||
const ob = this.afterSaleStatusList.filter(e => { return e.status === status });
|
|
||||||
if (ob.length) return ob[0].name
|
|
||||||
},
|
|
||||||
perviewImg (img) { // 查看图片
|
|
||||||
window.open(img, '_blank')
|
|
||||||
},
|
|
||||||
cancel (sn) { // 取消售后申请
|
|
||||||
Modal.confirm({
|
|
||||||
title: '取消',
|
|
||||||
content: '<p>确定取消此次售后申请吗?</p>',
|
|
||||||
onOk: () => {
|
|
||||||
cancelAfterSale(sn).then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
Message.success('取消售后申请成功')
|
|
||||||
this.getDetail()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
onCancel: () => {}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 获取物流公司列表
|
|
||||||
getCompany () {
|
|
||||||
getLogisticsCompany().then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
this.companyList = res.result
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// 提交物流信息
|
|
||||||
submitDelivery () {
|
|
||||||
this.submitLoading = true
|
|
||||||
afterSaleDelivery(this.form).then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
this.logisticsShow = false;
|
|
||||||
Message.success('提交成功')
|
|
||||||
this.getDetail()
|
|
||||||
}
|
|
||||||
this.submitLoading = false
|
|
||||||
}).catch(() => {
|
|
||||||
this.submitLoading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// 提交物流modal
|
|
||||||
openModal () {
|
|
||||||
this.logisticsShow = true;
|
|
||||||
if (!this.companyList.length) {
|
|
||||||
this.getCompany();
|
|
||||||
}
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.$refs.form?.resetFields();
|
|
||||||
this.form.afterSaleSn = this.afterSale.sn;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
this.getDetail();
|
|
||||||
this.getLog()
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
const submitLoading = ref(false); // 提交加载状态
|
||||||
|
|
||||||
|
const formRef = ref(null);
|
||||||
|
|
||||||
|
const canSubmitLogistics = computed(() => {
|
||||||
|
return afterSale.value.serviceStatus == 'PASS' && afterSale.value.serviceType != 'RETURN_MONEY'
|
||||||
|
});
|
||||||
|
const canCancel = computed(() => {
|
||||||
|
return afterSale.value.afterSaleAllowOperationVO && afterSale.value.afterSaleAllowOperationVO.cancel
|
||||||
|
});
|
||||||
|
|
||||||
|
function getDetail () { // 售后详情
|
||||||
|
afterSaleDetail(route.query.sn).then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
afterSale.value = res.result;
|
||||||
|
afterSale.value.serviceName = filterOrderStatus(afterSale.value.serviceStatus)
|
||||||
|
// 如果是原因id,则调接口查询原因中文释义
|
||||||
|
const pattern3 = new RegExp('[0-9]+');
|
||||||
|
if (pattern3.test(afterSale.value.reason)) {
|
||||||
|
getReason(afterSale.value.serviceType)
|
||||||
|
} else {
|
||||||
|
afterSale.value['reasonName'] = afterSale.value.reason
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function getReason (type) { // 获取售后原因
|
||||||
|
afterSaleReason(type).then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
reasonList.value = res.result
|
||||||
|
reasonList.value.forEach(e => {
|
||||||
|
if (e.id === afterSale.value.reason) {
|
||||||
|
afterSale.value['reasonName'] = e.reason
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function getLog () { // 获取售后日志
|
||||||
|
afterSaleLog(route.query.sn).then(res => {
|
||||||
|
logList.value = (res.result || []).slice().sort((a, b) => {
|
||||||
|
return new Date(a.createTime).getTime() - new Date(b.createTime).getTime()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function logDesc (log) {
|
||||||
|
const operator = log.operatorName ? `操作人:${log.operatorName}` : ''
|
||||||
|
return [operator, log.createTime].filter(Boolean).join(' ')
|
||||||
|
}
|
||||||
|
function shopPage (id) {
|
||||||
|
if (!id) return
|
||||||
|
router.push({
|
||||||
|
path: '/merchant',
|
||||||
|
query: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function filterOrderStatus (status) { // 获取订单状态中文
|
||||||
|
const ob = afterSaleStatusList.filter(e => { return e.status === status });
|
||||||
|
if (ob.length) return ob[0].name
|
||||||
|
}
|
||||||
|
function perviewImg (img) { // 查看图片
|
||||||
|
window.open(img, '_blank')
|
||||||
|
}
|
||||||
|
function cancel (sn) { // 取消售后申请
|
||||||
|
Modal.confirm({
|
||||||
|
title: '取消',
|
||||||
|
content: '<p>确定取消此次售后申请吗?</p>',
|
||||||
|
onOk: () => {
|
||||||
|
cancelAfterSale(sn).then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
Message.success('取消售后申请成功')
|
||||||
|
getDetail()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onCancel: () => {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 获取物流公司列表
|
||||||
|
function getCompany () {
|
||||||
|
getLogisticsCompany().then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
companyList.value = res.result
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 提交物流信息
|
||||||
|
function submitDelivery () {
|
||||||
|
submitLoading.value = true
|
||||||
|
afterSaleDelivery(form).then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
logisticsShow.value = false;
|
||||||
|
Message.success('提交成功')
|
||||||
|
getDetail()
|
||||||
|
}
|
||||||
|
submitLoading.value = false
|
||||||
|
}).catch(() => {
|
||||||
|
submitLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 提交物流modal
|
||||||
|
function openModal () {
|
||||||
|
logisticsShow.value = true;
|
||||||
|
if (!companyList.value.length) {
|
||||||
|
getCompany();
|
||||||
|
}
|
||||||
|
nextTick(() => {
|
||||||
|
formRef.value?.resetFields();
|
||||||
|
form.afterSaleSn = afterSale.value.sn;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getDetail();
|
||||||
|
getLog()
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.order-detail {
|
.order-detail {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
<el-table-column prop="num" label="购买数量" />
|
<el-table-column prop="num" label="购买数量" />
|
||||||
</el-table>
|
</el-table>
|
||||||
<div>
|
<div>
|
||||||
<el-form :model="form" ref="form" class="mt_10" :rules="rules" label-width="80px">
|
<el-form :model="form" ref="formRef" class="mt_10" :rules="rules" label-width="80px">
|
||||||
<el-form-item label="售后类别">
|
<el-form-item label="售后类别">
|
||||||
<el-radio-group v-model="form.serviceType" @change="changeReason" type="button" button-style="solid">
|
<el-radio-group v-model="form.serviceType" @change="changeReason" type="button" button-style="solid">
|
||||||
<el-radio v-if="info.returnGoods" label="RETURN_GOODS">退货</el-radio>
|
<el-radio v-if="info.returnGoods" label="RETURN_GOODS">退货</el-radio>
|
||||||
@@ -101,125 +101,125 @@
|
|||||||
</UserCenterLayout>
|
</UserCenterLayout>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
|
import { nextTick, onMounted, reactive, ref } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { Message, Notice } from "@/utils/message";
|
import { Message, Notice } from "@/utils/message";
|
||||||
import { Camera, Delete, View } from '@element-plus/icons-vue';
|
import { Camera, Delete, View } from '@element-plus/icons-vue';
|
||||||
import { afterSaleReason, afterSaleInfo, applyAfterSale } from '@/api/member.js'
|
import { afterSaleReason, afterSaleInfo, applyAfterSale } from '@/api/member.js'
|
||||||
import { buyerUrl } from '@/plugins/request.js';
|
import { buyerUrl } from '@/plugins/request.js';
|
||||||
import storage from '@/plugins/storage';
|
import storage from '@/plugins/storage';
|
||||||
import * as RegExp from '@/plugins/RegExp'
|
import * as RegExp from '@/plugins/RegExp'
|
||||||
export default {
|
|
||||||
components: { Camera, Delete, View },
|
const route = useRoute();
|
||||||
data () {
|
const router = useRouter();
|
||||||
const checkNum = (rule, value, callback) => {
|
|
||||||
if (value === '') {
|
const formRef = ref(null);
|
||||||
callback(new Error('请填写提交数量'));
|
|
||||||
} else if (value > this.info.num) {
|
const goodsData = ref([]); // 商品数据
|
||||||
callback(new Error('提交数量不能大于购买数量'));
|
const reasonList = ref([]); // 售后原因列表
|
||||||
} else if (!RegExp.integer.test(value)) {
|
const info = ref({}); // 售后信息
|
||||||
callback(new Error('提交数量必须为正整数'));
|
const form = reactive({ // 售后数据
|
||||||
} else {
|
serviceType: 'RETURN_GOODS',
|
||||||
callback();
|
num: 1
|
||||||
}
|
});
|
||||||
};
|
const uploadList = ref([]); // 上传列表
|
||||||
return {
|
const action = buyerUrl + '/buyer/common/upload/file'; // 上传地址
|
||||||
goodsData: [], // 商品数据
|
const accessToken = reactive({}); // 验证token
|
||||||
reasonList: [], // 售后原因列表
|
const visible = ref(false); // 图片预览
|
||||||
info: {}, // 售后信息
|
const previewImage = ref(''); // 预览图片url
|
||||||
form: { // 售后数据
|
|
||||||
serviceType: 'RETURN_GOODS',
|
const checkNum = (rule, value, callback) => {
|
||||||
num: 1
|
if (value === '') {
|
||||||
},
|
callback(new Error('请填写提交数量'));
|
||||||
uploadList: [], // 上传列表
|
} else if (value > info.value.num) {
|
||||||
action: buyerUrl + '/buyer/common/upload/file', // 上传地址
|
callback(new Error('提交数量不能大于购买数量'));
|
||||||
accessToken: {}, // 验证token
|
} else if (!RegExp.integer.test(value)) {
|
||||||
visible: false, // 图片预览
|
callback(new Error('提交数量必须为正整数'));
|
||||||
previewImage: '', // 预览图片url
|
} else {
|
||||||
rules: { // 验证规则
|
callback();
|
||||||
num: [{ validator: checkNum }],
|
}
|
||||||
reason: [{ required: true, message: '请选择提交原因' }],
|
};
|
||||||
problemDesc: [{ required: true, message: '请输入问题描述' }],
|
const rules = { // 验证规则
|
||||||
bankAccountNumber: [
|
num: [{ validator: checkNum }],
|
||||||
{ required: true, message: '请输入银行卡号' },
|
reason: [{ required: true, message: '请选择提交原因' }],
|
||||||
{
|
problemDesc: [{ required: true, message: '请输入问题描述' }],
|
||||||
type: 'string',
|
bankAccountNumber: [
|
||||||
pattern: /^[0-9]\d*$/,
|
{ required: true, message: '请输入银行卡号' },
|
||||||
message: '请输入正确的银行卡号'
|
{
|
||||||
}
|
type: 'string',
|
||||||
],
|
pattern: /^[0-9]\d*$/,
|
||||||
bankAccountName: [{ required: true, message: '请输入银行开户名' }],
|
message: '请输入正确的银行卡号'
|
||||||
bankDepositName: [{ required: true, message: '请输入银行开户行' }]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
],
|
||||||
methods: {
|
bankAccountName: [{ required: true, message: '请输入银行开户名' }],
|
||||||
getInfo () { // 获取售后信息
|
bankDepositName: [{ required: true, message: '请输入银行开户行' }]
|
||||||
afterSaleInfo(this.$route.query.sn).then(res => {
|
};
|
||||||
|
|
||||||
|
function getInfo () { // 获取售后信息
|
||||||
|
afterSaleInfo(route.query.sn).then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
info.value = res.result
|
||||||
|
goodsData.value.push(res.result)
|
||||||
|
if (!info.value.returnGoods && info.value.returnMoney) {
|
||||||
|
form.serviceType = 'RETURN_MONEY'
|
||||||
|
}
|
||||||
|
getReason(form.serviceType)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function getReason (type) { // 获取售后原因
|
||||||
|
afterSaleReason(type).then(res => {
|
||||||
|
if (res.success) reasonList.value = res.result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function changeReason (type) { // 改变售后原因列表
|
||||||
|
form.reason = undefined
|
||||||
|
getReason(type)
|
||||||
|
nextTick(() => {
|
||||||
|
formRef.value?.clearValidate('reason')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function apply () { // 售后申请提交
|
||||||
|
formRef.value.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
let params = Object.assign(info.value, form)
|
||||||
|
params.images = uploadList.value.toString()
|
||||||
|
params.orderItemSn = route.query.sn
|
||||||
|
params.reason = reasonList.value.find(item => item.id == params.reason).reason
|
||||||
|
applyAfterSale(params).then(res => {
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
this.info = res.result
|
Message.success('售后申请提交成功,请到售后订单查看!')
|
||||||
this.goodsData.push(res.result)
|
router.push({name: 'AfterSale'})
|
||||||
if (!this.info.returnGoods && this.info.returnMoney) {
|
|
||||||
this.form.serviceType = 'RETURN_MONEY'
|
|
||||||
}
|
|
||||||
this.getReason(this.form.serviceType)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
|
||||||
getReason (type) { // 获取售后原因
|
|
||||||
afterSaleReason(type).then(res => {
|
|
||||||
if (res.success) this.reasonList = res.result
|
|
||||||
})
|
|
||||||
},
|
|
||||||
changeReason (type) { // 改变售后原因列表
|
|
||||||
this.form.reason = undefined
|
|
||||||
this.getReason(type)
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.$refs.form?.clearValidate('reason')
|
|
||||||
})
|
|
||||||
},
|
|
||||||
apply () { // 售后申请提交
|
|
||||||
this.$refs.form.validate(valid => {
|
|
||||||
if (valid) {
|
|
||||||
let params = Object.assign(this.info, this.form)
|
|
||||||
params.images = this.uploadList.toString()
|
|
||||||
params.orderItemSn = this.$route.query.sn
|
|
||||||
params.reason = this.reasonList.find(item => item.id == params.reason).reason
|
|
||||||
applyAfterSale(params).then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
Message.success('售后申请提交成功,请到售后订单查看!')
|
|
||||||
this.$router.push({name: 'AfterSale'})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
handleView (name) { // 预览图片
|
|
||||||
this.previewImage = name;
|
|
||||||
this.visible = true;
|
|
||||||
},
|
|
||||||
handleRemove (index) { // 移除图片
|
|
||||||
this.uploadList.splice(index, 1)
|
|
||||||
this.$forceUpdate()
|
|
||||||
},
|
|
||||||
handleSuccess (res, file) { // 上传成功回调
|
|
||||||
this.uploadList.push(res.result)
|
|
||||||
this.$forceUpdate()
|
|
||||||
},
|
|
||||||
handleBeforeUpload () { // 上传之前钩子函数
|
|
||||||
const check = this.uploadList.length < 6;
|
|
||||||
if (!check) {
|
|
||||||
Notice.warning({
|
|
||||||
title: '最多可以上传5张图片'
|
|
||||||
});
|
|
||||||
return check;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
mounted () {
|
}
|
||||||
this.accessToken.accessToken = storage.getItem('accessToken');
|
function handleView (name) { // 预览图片
|
||||||
this.getInfo()
|
previewImage.value = name;
|
||||||
|
visible.value = true;
|
||||||
|
}
|
||||||
|
function handleRemove (index) { // 移除图片
|
||||||
|
uploadList.value.splice(index, 1)
|
||||||
|
}
|
||||||
|
function handleSuccess (res, file) { // 上传成功回调
|
||||||
|
uploadList.value.push(res.result)
|
||||||
|
}
|
||||||
|
function handleBeforeUpload () { // 上传之前钩子函数
|
||||||
|
const check = uploadList.value.length < 6;
|
||||||
|
if (!check) {
|
||||||
|
Notice.warning({
|
||||||
|
title: '最多可以上传5张图片'
|
||||||
|
});
|
||||||
|
return check;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
accessToken.accessToken = storage.getItem('accessToken');
|
||||||
|
getInfo()
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
|||||||
@@ -61,101 +61,107 @@
|
|||||||
</UserCenterLayout>
|
</UserCenterLayout>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
|
import { getCurrentInstance, onMounted, reactive, ref } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { Message, Notice } from "@/utils/message";
|
import { Message, Notice } from "@/utils/message";
|
||||||
import { Camera, Delete, View } from '@element-plus/icons-vue';
|
import { Camera, Delete, View } from '@element-plus/icons-vue';
|
||||||
import { orderDetail } from '@/api/order.js';
|
import { orderDetail } from '@/api/order.js';
|
||||||
import { afterSaleReason, handleComplain } from '@/api/member.js';
|
import { afterSaleReason, handleComplain } from '@/api/member.js';
|
||||||
import { buyerUrl } from '@/plugins/request.js';
|
import { buyerUrl } from '@/plugins/request.js';
|
||||||
import storage from '@/plugins/storage';
|
import storage from '@/plugins/storage';
|
||||||
export default {
|
|
||||||
components: { Camera, Delete, View },
|
const route = useRoute();
|
||||||
data () {
|
const router = useRouter();
|
||||||
return {
|
const instance = getCurrentInstance();
|
||||||
order: {}, // 订单详情
|
|
||||||
orderGoods: {}, // 订单商品
|
const order = ref({}); // 订单详情
|
||||||
form: { // 投诉表单
|
const orderGoods = ref({}); // 订单商品
|
||||||
complainTopic: '',
|
const form = reactive({ // 投诉表单
|
||||||
content: ''
|
complainTopic: '',
|
||||||
}, // 表单
|
content: ''
|
||||||
visible: false, // 图片预览
|
}); // 表单
|
||||||
action: buyerUrl + '/buyer/common/upload/file', // 上传地址
|
const visible = ref(false); // 图片预览
|
||||||
accessToken: {}, // 验证token
|
const action = ref(buyerUrl + '/buyer/common/upload/file'); // 上传地址
|
||||||
previewImage: '', // 图片地址
|
const accessToken = reactive({}); // 验证token
|
||||||
loading: false, // 加载状态
|
const previewImage = ref(''); // 图片地址
|
||||||
reasonList: [] // 投诉原因
|
const loading = ref(false); // 加载状态
|
||||||
|
const reasonList = ref([]); // 投诉原因
|
||||||
|
|
||||||
|
function getOrderDetail() { // 获取订单详情
|
||||||
|
orderDetail(route.query.sn).then(res => {
|
||||||
|
order.value = res.result
|
||||||
|
orderGoods.value = res.result.orderItems[route.query.index]
|
||||||
|
orderGoods.value.uploadList = []
|
||||||
|
})
|
||||||
|
afterSaleReason('COMPLAIN').then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
reasonList.value = res.result
|
||||||
|
form['complainTopic'] = res.result[0].reason
|
||||||
|
instance.proxy.$forceUpdate()
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
methods: {
|
}
|
||||||
getOrderDetail () { // 获取订单详情
|
|
||||||
orderDetail(this.$route.query.sn).then(res => {
|
function save() { // 提交投诉信息
|
||||||
this.order = res.result
|
if (!form.content || !String(form.content).trim()) {
|
||||||
this.orderGoods = res.result.orderItems[this.$route.query.index]
|
Message.warning('投诉内容不能为空')
|
||||||
this.orderGoods.uploadList = []
|
return
|
||||||
})
|
}
|
||||||
afterSaleReason('COMPLAIN').then(res => {
|
let params = {
|
||||||
if (res.success) {
|
goodsId: orderGoods.value.goodsId,
|
||||||
this.reasonList = res.result
|
complainTopic: form.complainTopic,
|
||||||
this.form['complainTopic'] = res.result[0].reason
|
content: form.content,
|
||||||
this.$forceUpdate()
|
images: orderGoods.value.uploadList.toString(),
|
||||||
}
|
orderSn: route.query.sn,
|
||||||
})
|
skuId: orderGoods.value.skuId
|
||||||
},
|
}
|
||||||
save () { // 提交投诉信息
|
handleComplain(params).then(res => {
|
||||||
if (!this.form.content || !String(this.form.content).trim()) {
|
if (res.success) {
|
||||||
Message.warning('投诉内容不能为空')
|
Message.success('投诉申请已提交,感谢您的反馈')
|
||||||
return
|
router.push({name: 'ComplainList'})
|
||||||
}
|
|
||||||
let params = {
|
|
||||||
goodsId: this.orderGoods.goodsId,
|
|
||||||
complainTopic: this.form.complainTopic,
|
|
||||||
content: this.form.content,
|
|
||||||
images: this.orderGoods.uploadList.toString(),
|
|
||||||
orderSn: this.$route.query.sn,
|
|
||||||
skuId: this.orderGoods.skuId
|
|
||||||
}
|
|
||||||
handleComplain(params).then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
Message.success('投诉申请已提交,感谢您的反馈')
|
|
||||||
this.$router.push({name: 'ComplainList'})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
goGoodsDetail (skuId, goodsId) { // 跳转商品详情
|
|
||||||
let routerUrl = this.$router.resolve({
|
|
||||||
path: '/goodsDetail',
|
|
||||||
query: {skuId, goodsId}
|
|
||||||
})
|
|
||||||
window.open(routerUrl.href, '_blank')
|
|
||||||
},
|
|
||||||
handleView (name) { // 预览图片
|
|
||||||
this.previewImage = name;
|
|
||||||
this.visible = true;
|
|
||||||
},
|
|
||||||
handleRemove (index) { // 移除图片
|
|
||||||
this.orderGoods.uploadList.splice(index, 1)
|
|
||||||
this.$forceUpdate()
|
|
||||||
},
|
|
||||||
handleSuccess (res, file) { // 上传成功回调
|
|
||||||
this.orderGoods.uploadList.push(res.result)
|
|
||||||
this.$forceUpdate()
|
|
||||||
},
|
|
||||||
handleBeforeUpload () { // 上传之前钩子函数
|
|
||||||
const check = this.orderGoods.uploadList.length < 6;
|
|
||||||
if (!check) {
|
|
||||||
Notice.warning({
|
|
||||||
title: '最多可以上传五张图片'
|
|
||||||
});
|
|
||||||
return check;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
mounted () {
|
}
|
||||||
this.accessToken.accessToken = storage.getItem('accessToken');
|
|
||||||
this.getOrderDetail()
|
function goGoodsDetail(skuId, goodsId) { // 跳转商品详情
|
||||||
window.scrollTo(0, 0)
|
let routerUrl = router.resolve({
|
||||||
|
path: '/goodsDetail',
|
||||||
|
query: {skuId, goodsId}
|
||||||
|
})
|
||||||
|
window.open(routerUrl.href, '_blank')
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleView(name) { // 预览图片
|
||||||
|
previewImage.value = name;
|
||||||
|
visible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleRemove(index) { // 移除图片
|
||||||
|
orderGoods.value.uploadList.splice(index, 1)
|
||||||
|
instance.proxy.$forceUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSuccess(res, file) { // 上传成功回调
|
||||||
|
orderGoods.value.uploadList.push(res.result)
|
||||||
|
instance.proxy.$forceUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleBeforeUpload() { // 上传之前钩子函数
|
||||||
|
const check = orderGoods.value.uploadList.length < 6;
|
||||||
|
if (!check) {
|
||||||
|
Notice.warning({
|
||||||
|
title: '最多可以上传五张图片'
|
||||||
|
});
|
||||||
|
return check;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
accessToken.accessToken = storage.getItem('accessToken');
|
||||||
|
getOrderDetail()
|
||||||
|
window.scrollTo(0, 0)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.delivery-rate {
|
.delivery-rate {
|
||||||
|
|||||||
@@ -35,59 +35,59 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { ref, onMounted } from "vue";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
import { Message, Modal } from "@/utils/message";
|
import { Message, Modal } from "@/utils/message";
|
||||||
import { Delete, Edit } from '@element-plus/icons-vue';
|
import { Delete, Edit } from '@element-plus/icons-vue';
|
||||||
import { memberAddress, delMemberAddress } from '@/api/address.js';
|
import { memberAddress, delMemberAddress } from '@/api/address.js';
|
||||||
|
|
||||||
export default {
|
defineOptions({ name: 'MyAddress' });
|
||||||
components: { Delete, Edit },
|
|
||||||
name: 'MyAddress',
|
|
||||||
|
|
||||||
data () {
|
const router = useRouter();
|
||||||
return {
|
|
||||||
list: [] // 地址列表
|
const list = ref([]); // 地址列表
|
||||||
};
|
|
||||||
},
|
function add () {
|
||||||
methods: {
|
router.push('/home/addAddress');
|
||||||
add () {
|
}
|
||||||
this.$router.push('/home/addAddress');
|
|
||||||
},
|
// 编辑地址
|
||||||
edit (id) {
|
function edit (id) {
|
||||||
// 编辑地址
|
router.push({ path: '/home/addAddress', query: { id: id } });
|
||||||
this.$router.push({ path: '/home/addAddress', query: { id: id } });
|
}
|
||||||
},
|
|
||||||
del (id) {
|
// 删除地址
|
||||||
// 删除地址
|
function del (id) {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: '提示',
|
title: '提示',
|
||||||
content: '你确定删除这个收货地址',
|
content: '你确定删除这个收货地址',
|
||||||
onOk: () => {
|
onOk: () => {
|
||||||
delMemberAddress(id).then((res) => {
|
delMemberAddress(id).then((res) => {
|
||||||
if (res.success) {
|
|
||||||
Message.success('删除成功');
|
|
||||||
this.getAddrList();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onCancel: () => {
|
|
||||||
Message.info('取消删除');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getAddrList () {
|
|
||||||
// 获取地址列表
|
|
||||||
memberAddress().then((res) => {
|
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
this.list = res.result.records;
|
Message.success('删除成功');
|
||||||
|
getAddrList();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
onCancel: () => {
|
||||||
|
Message.info('取消删除');
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
mounted () {
|
}
|
||||||
this.getAddrList();
|
|
||||||
}
|
// 获取地址列表
|
||||||
};
|
function getAddrList () {
|
||||||
|
memberAddress().then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
list.value = res.result.records;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getAddrList();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -197,266 +197,264 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { onMounted, reactive, ref, watch } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { Message, Modal } from "@/utils/message";
|
import { Message, Modal } from "@/utils/message";
|
||||||
import { getOrderList, sureReceived, cancelOrder, delOrder } from '@/api/order';
|
import { getOrderList, sureReceived, cancelOrder, delOrder as deleteOrder } from '@/api/order';
|
||||||
import { afterSaleReason } from '@/api/member';
|
import { afterSaleReason } from '@/api/member';
|
||||||
import { orderStatusList, afterSaleStatusList } from '../enumeration.js'
|
import { orderStatusList, afterSaleStatusList } from '../enumeration.js'
|
||||||
export default {
|
|
||||||
name: 'MyOrder',
|
defineOptions({ name: 'MyOrder' });
|
||||||
props: {
|
|
||||||
homePage: { // 判断是否个人中心首页展示内容
|
const props = defineProps({
|
||||||
type: Boolean,
|
homePage: { // 判断是否个人中心首页展示内容
|
||||||
default: false
|
type: Boolean,
|
||||||
}
|
default: false
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
orderList: [], // 订单列表
|
|
||||||
params: { // 请求参数
|
|
||||||
pageNumber: 1,
|
|
||||||
pageSize: 20,
|
|
||||||
// orderStatus: 'ALL',
|
|
||||||
keywords: '',
|
|
||||||
tag: 'ALL'
|
|
||||||
},
|
|
||||||
cancelParams: { // 取消售后参数
|
|
||||||
orderSn: '',
|
|
||||||
reason: ''
|
|
||||||
},
|
|
||||||
// 状态数组
|
|
||||||
orderStatusList,
|
|
||||||
afterSaleStatusList,
|
|
||||||
changeWay: ['全部', '待付款', '待发货', '待收货/使用'],
|
|
||||||
activeOrderTab: 0,
|
|
||||||
total: 0, // 数据总数
|
|
||||||
spinShow: false, // 加载状态
|
|
||||||
afterSaleModal: false, // 选择售后商品模态框
|
|
||||||
afterSaleArr: [], // 售后商品列表
|
|
||||||
cancelAvail: false, // 取消订单modal控制
|
|
||||||
cancelReason: [] // 取消订单原因
|
|
||||||
};
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
if (this.homePage) this.params.pageSize = 5;
|
|
||||||
if (!this.homePage) this.applyRouteTag();
|
|
||||||
this.getList();
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
'$route.query.tag' () {
|
|
||||||
if (this.homePage || this.$route.name !== 'MyOrder') return;
|
|
||||||
const next = this.$route.query.tag || 'ALL';
|
|
||||||
if (this.params.tag === next) return;
|
|
||||||
this.applyRouteTag();
|
|
||||||
this.getList();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// 退款状态枚举
|
|
||||||
refundPriceList(status) {
|
|
||||||
switch (status) {
|
|
||||||
case 'ALL_REFUND':
|
|
||||||
return "全部退款";
|
|
||||||
case 'PART_REFUND':
|
|
||||||
return "部分退款";
|
|
||||||
case 'NO_REFUND':
|
|
||||||
return "";
|
|
||||||
case 'REFUNDING':
|
|
||||||
return "退款中";
|
|
||||||
default:
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
goodsDetail (skuId, goodsId) {
|
|
||||||
// 跳转商品详情
|
|
||||||
let routeUrl = this.$router.resolve({
|
|
||||||
path: '/goodsDetail',
|
|
||||||
query: { skuId, goodsId }
|
|
||||||
});
|
|
||||||
window.open(routeUrl.href, '_blank');
|
|
||||||
},
|
|
||||||
orderTags () {
|
|
||||||
return ['ALL', 'WAIT_PAY', 'WAIT_SHIP', 'WAIT_USE'];
|
|
||||||
},
|
|
||||||
applyRouteTag () {
|
|
||||||
const tags = this.orderTags();
|
|
||||||
const tag = this.$route.query.tag;
|
|
||||||
const index = tags.indexOf(tag);
|
|
||||||
this.activeOrderTab = index > -1 ? index : 0;
|
|
||||||
this.params.tag = tags[this.activeOrderTab];
|
|
||||||
this.params.pageNumber = 1;
|
|
||||||
},
|
|
||||||
// 切换订单状态
|
|
||||||
change (index) {
|
|
||||||
this.activeOrderTab = index;
|
|
||||||
this.params.pageNumber = 1;
|
|
||||||
delete this.params.commentStatus;
|
|
||||||
const tags = this.orderTags();
|
|
||||||
this.params.tag = tags[index] || 'ALL';
|
|
||||||
const query = { ...this.$route.query };
|
|
||||||
if (index === 0) delete query.tag;
|
|
||||||
else query.tag = this.params.tag;
|
|
||||||
const current = this.$route.query.tag || 'ALL';
|
|
||||||
if (current !== this.params.tag) {
|
|
||||||
this.$router.replace({ name: 'MyOrder', query });
|
|
||||||
}
|
|
||||||
this.getList()
|
|
||||||
},
|
|
||||||
// 跳转店铺首页
|
|
||||||
shopPage (id) {
|
|
||||||
this.$router.push({
|
|
||||||
path: '/merchant',
|
|
||||||
query: { id: id }
|
|
||||||
});
|
|
||||||
},
|
|
||||||
orderDetail (sn) {
|
|
||||||
this.$router.push({ name: 'OrderDetail', query: {sn} });
|
|
||||||
},
|
|
||||||
goAfterSaleDetail (sn) {
|
|
||||||
this.$router.push({ name: 'AfterSaleDetail', query: { sn } });
|
|
||||||
},
|
|
||||||
received (sn) { // 确认收货
|
|
||||||
sureReceived(sn).then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
Message.success('确认收货成功')
|
|
||||||
this.getList()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
goPay (sn) { // 去支付
|
|
||||||
this.$router.push({path: '/payment', query: {orderType: 'ORDER', sn}});
|
|
||||||
},
|
|
||||||
applyAfterSale (goodsItem) { // 申请售后
|
|
||||||
let arr = []
|
|
||||||
goodsItem.forEach(e => {
|
|
||||||
if (e.afterSaleStatus === 'NOT_APPLIED'|| e.afterSaleStatus === 'PART_AFTER_SALE') {
|
|
||||||
arr.push(e)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (arr.length === 1) {
|
|
||||||
this.$router.push({name: 'ApplyAfterSale', query: {sn: arr[0].sn}})
|
|
||||||
} else {
|
|
||||||
this.afterSaleArr = arr;
|
|
||||||
this.afterSaleModal = true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 申请售后
|
|
||||||
afterSaleSelect (item) {
|
|
||||||
this.$router.push({name: 'ApplyAfterSale', query: {sn: item.sn}})
|
|
||||||
},
|
|
||||||
complain (sn, goodsIndex) { // 投诉
|
|
||||||
this.$router.push({name: 'Complain', query: {sn, index: goodsIndex}})
|
|
||||||
},
|
|
||||||
delOrder (sn) { // 删除订单
|
|
||||||
Modal.confirm({
|
|
||||||
title: '删除订单',
|
|
||||||
content: '<p>确认删除当前订单吗?</p>',
|
|
||||||
onOk: () => {
|
|
||||||
delOrder(sn).then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
Message.success('删除成功');
|
|
||||||
this.getList()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
onCancel: () => {}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getList () {
|
|
||||||
this.spinShow = true;
|
|
||||||
let params = JSON.parse(JSON.stringify(this.params))
|
|
||||||
if (params.orderStatus === 'ALL') {
|
|
||||||
delete params.orderStatus
|
|
||||||
}
|
|
||||||
getOrderList(params).then(res => {
|
|
||||||
this.spinShow = false
|
|
||||||
if (res.success) {
|
|
||||||
this.orderList = res.result.records;
|
|
||||||
this.total = res.result.total;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
mapAfterSaleOrder (item) {
|
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
isAfterSale: true,
|
|
||||||
serviceSn: item.sn,
|
|
||||||
sn: item.orderSn,
|
|
||||||
orderStatus: item.serviceStatus,
|
|
||||||
flowPrice: item.applyRefundPrice,
|
|
||||||
orderItems: [
|
|
||||||
{
|
|
||||||
image: item.goodsImage,
|
|
||||||
name: item.goodsName,
|
|
||||||
skuId: item.skuId,
|
|
||||||
goodsId: item.goodsId,
|
|
||||||
goodsPrice: item.flowPrice,
|
|
||||||
num: item.num,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
allowOperationVO: {},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
changePageNum (val) { // 修改页码
|
|
||||||
this.params.pageNumber = val;
|
|
||||||
this.getList()
|
|
||||||
},
|
|
||||||
changePageSize (val) { // 修改页数
|
|
||||||
this.params.pageNumber = 1;
|
|
||||||
this.params.pageSize = val;
|
|
||||||
this.getList()
|
|
||||||
},
|
|
||||||
handleCancelOrder (sn) {
|
|
||||||
// 取消订单
|
|
||||||
this.cancelParams.orderSn = sn;
|
|
||||||
afterSaleReason('CANCEL').then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
this.cancelReason = res.result;
|
|
||||||
this.cancelAvail = true
|
|
||||||
this.cancelParams.reason = this.cancelReason[0].reason
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
sureCancel () { // 确定取消
|
|
||||||
cancelOrder(this.cancelParams).then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
Message.success('取消订单成功')
|
|
||||||
this.getList()
|
|
||||||
this.cancelAvail = false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
filterOrderStatus (status) { // 获取订单状态中文
|
|
||||||
const ob = this.orderStatusList.filter(e => { return e.status === status });
|
|
||||||
return ob && ob[0] ? ob[0].name : status
|
|
||||||
},
|
|
||||||
filterAfterSaleStatus (status) {
|
|
||||||
const ob = this.afterSaleStatusList.filter(e => e.status === status);
|
|
||||||
return ob && ob[0] ? ob[0].name : status
|
|
||||||
},
|
|
||||||
canAfterSale (order) {
|
|
||||||
if (order.isAfterSale || order.orderType === 'E_COUPON' || !order.groupAfterSaleStatus) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return order.groupAfterSaleStatus.includes('NOT_APPLIED') || order.groupAfterSaleStatus.includes('PART_AFTER_SALE')
|
|
||||||
},
|
|
||||||
paymentLabel (order) {
|
|
||||||
const map = {
|
|
||||||
WECHAT: '微信支付',
|
|
||||||
ALIPAY: '支付宝',
|
|
||||||
WALLET: '余额支付',
|
|
||||||
BANK_TRANSFER: '线下转账'
|
|
||||||
}
|
|
||||||
if (order.paymentMethod && map[order.paymentMethod]) {
|
|
||||||
return map[order.paymentMethod]
|
|
||||||
}
|
|
||||||
if (order.payStatus === 'UNPAID' || order.orderStatus === 'UNPAID') {
|
|
||||||
return '待支付'
|
|
||||||
}
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const orderList = ref([]); // 订单列表
|
||||||
|
const params = reactive({ // 请求参数
|
||||||
|
pageNumber: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
// orderStatus: 'ALL',
|
||||||
|
keywords: '',
|
||||||
|
tag: 'ALL'
|
||||||
|
});
|
||||||
|
const cancelParams = reactive({ // 取消售后参数
|
||||||
|
orderSn: '',
|
||||||
|
reason: ''
|
||||||
|
});
|
||||||
|
const changeWay = ref(['全部', '待付款', '待发货', '待收货/使用']);
|
||||||
|
const activeOrderTab = ref(0);
|
||||||
|
const total = ref(0); // 数据总数
|
||||||
|
const spinShow = ref(false); // 加载状态
|
||||||
|
const afterSaleModal = ref(false); // 选择售后商品模态框
|
||||||
|
const afterSaleArr = ref([]); // 售后商品列表
|
||||||
|
const cancelAvail = ref(false); // 取消订单modal控制
|
||||||
|
const cancelReason = ref([]); // 取消订单原因
|
||||||
|
|
||||||
|
// 退款状态枚举
|
||||||
|
function refundPriceList(status) {
|
||||||
|
switch (status) {
|
||||||
|
case 'ALL_REFUND':
|
||||||
|
return "全部退款";
|
||||||
|
case 'PART_REFUND':
|
||||||
|
return "部分退款";
|
||||||
|
case 'NO_REFUND':
|
||||||
|
return "";
|
||||||
|
case 'REFUNDING':
|
||||||
|
return "退款中";
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function goodsDetail (skuId, goodsId) {
|
||||||
|
// 跳转商品详情
|
||||||
|
let routeUrl = router.resolve({
|
||||||
|
path: '/goodsDetail',
|
||||||
|
query: { skuId, goodsId }
|
||||||
|
});
|
||||||
|
window.open(routeUrl.href, '_blank');
|
||||||
|
}
|
||||||
|
function orderTags () {
|
||||||
|
return ['ALL', 'WAIT_PAY', 'WAIT_SHIP', 'WAIT_USE'];
|
||||||
|
}
|
||||||
|
function applyRouteTag () {
|
||||||
|
const tags = orderTags();
|
||||||
|
const tag = route.query.tag;
|
||||||
|
const index = tags.indexOf(tag);
|
||||||
|
activeOrderTab.value = index > -1 ? index : 0;
|
||||||
|
params.tag = tags[activeOrderTab.value];
|
||||||
|
params.pageNumber = 1;
|
||||||
|
}
|
||||||
|
// 切换订单状态
|
||||||
|
function change (index) {
|
||||||
|
activeOrderTab.value = index;
|
||||||
|
params.pageNumber = 1;
|
||||||
|
delete params.commentStatus;
|
||||||
|
const tags = orderTags();
|
||||||
|
params.tag = tags[index] || 'ALL';
|
||||||
|
const query = { ...route.query };
|
||||||
|
if (index === 0) delete query.tag;
|
||||||
|
else query.tag = params.tag;
|
||||||
|
const current = route.query.tag || 'ALL';
|
||||||
|
if (current !== params.tag) {
|
||||||
|
router.replace({ name: 'MyOrder', query });
|
||||||
|
}
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
// 跳转店铺首页
|
||||||
|
function shopPage (id) {
|
||||||
|
router.push({
|
||||||
|
path: '/merchant',
|
||||||
|
query: { id: id }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function orderDetail (sn) {
|
||||||
|
router.push({ name: 'OrderDetail', query: {sn} });
|
||||||
|
}
|
||||||
|
function goAfterSaleDetail (sn) {
|
||||||
|
router.push({ name: 'AfterSaleDetail', query: { sn } });
|
||||||
|
}
|
||||||
|
function received (sn) { // 确认收货
|
||||||
|
sureReceived(sn).then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
Message.success('确认收货成功')
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function goPay (sn) { // 去支付
|
||||||
|
router.push({path: '/payment', query: {orderType: 'ORDER', sn}});
|
||||||
|
}
|
||||||
|
function applyAfterSale (goodsItem) { // 申请售后
|
||||||
|
let arr = []
|
||||||
|
goodsItem.forEach(e => {
|
||||||
|
if (e.afterSaleStatus === 'NOT_APPLIED'|| e.afterSaleStatus === 'PART_AFTER_SALE') {
|
||||||
|
arr.push(e)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (arr.length === 1) {
|
||||||
|
router.push({name: 'ApplyAfterSale', query: {sn: arr[0].sn}})
|
||||||
|
} else {
|
||||||
|
afterSaleArr.value = arr;
|
||||||
|
afterSaleModal.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 申请售后
|
||||||
|
function afterSaleSelect (item) {
|
||||||
|
router.push({name: 'ApplyAfterSale', query: {sn: item.sn}})
|
||||||
|
}
|
||||||
|
function complain (sn, goodsIndex) { // 投诉
|
||||||
|
router.push({name: 'Complain', query: {sn, index: goodsIndex}})
|
||||||
|
}
|
||||||
|
function delOrder (sn) { // 删除订单
|
||||||
|
Modal.confirm({
|
||||||
|
title: '删除订单',
|
||||||
|
content: '<p>确认删除当前订单吗?</p>',
|
||||||
|
onOk: () => {
|
||||||
|
deleteOrder(sn).then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
Message.success('删除成功');
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onCancel: () => {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function getList () {
|
||||||
|
spinShow.value = true;
|
||||||
|
let _params = JSON.parse(JSON.stringify(params))
|
||||||
|
if (_params.orderStatus === 'ALL') {
|
||||||
|
delete _params.orderStatus
|
||||||
|
}
|
||||||
|
getOrderList(_params).then(res => {
|
||||||
|
spinShow.value = false
|
||||||
|
if (res.success) {
|
||||||
|
orderList.value = res.result.records;
|
||||||
|
total.value = res.result.total;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function mapAfterSaleOrder (item) {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
isAfterSale: true,
|
||||||
|
serviceSn: item.sn,
|
||||||
|
sn: item.orderSn,
|
||||||
|
orderStatus: item.serviceStatus,
|
||||||
|
flowPrice: item.applyRefundPrice,
|
||||||
|
orderItems: [
|
||||||
|
{
|
||||||
|
image: item.goodsImage,
|
||||||
|
name: item.goodsName,
|
||||||
|
skuId: item.skuId,
|
||||||
|
goodsId: item.goodsId,
|
||||||
|
goodsPrice: item.flowPrice,
|
||||||
|
num: item.num,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
allowOperationVO: {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function changePageNum (val) { // 修改页码
|
||||||
|
params.pageNumber = val;
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
function changePageSize (val) { // 修改页数
|
||||||
|
params.pageNumber = 1;
|
||||||
|
params.pageSize = val;
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
function handleCancelOrder (sn) {
|
||||||
|
// 取消订单
|
||||||
|
cancelParams.orderSn = sn;
|
||||||
|
afterSaleReason('CANCEL').then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
cancelReason.value = res.result;
|
||||||
|
cancelAvail.value = true
|
||||||
|
cancelParams.reason = cancelReason.value[0].reason
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function sureCancel () { // 确定取消
|
||||||
|
cancelOrder(cancelParams).then(res => {
|
||||||
|
if (res.success) {
|
||||||
|
Message.success('取消订单成功')
|
||||||
|
getList()
|
||||||
|
cancelAvail.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function filterOrderStatus (status) { // 获取订单状态中文
|
||||||
|
const ob = orderStatusList.filter(e => { return e.status === status });
|
||||||
|
return ob && ob[0] ? ob[0].name : status
|
||||||
|
}
|
||||||
|
function filterAfterSaleStatus (status) {
|
||||||
|
const ob = afterSaleStatusList.filter(e => e.status === status);
|
||||||
|
return ob && ob[0] ? ob[0].name : status
|
||||||
|
}
|
||||||
|
function canAfterSale (order) {
|
||||||
|
if (order.isAfterSale || order.orderType === 'E_COUPON' || !order.groupAfterSaleStatus) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return order.groupAfterSaleStatus.includes('NOT_APPLIED') || order.groupAfterSaleStatus.includes('PART_AFTER_SALE')
|
||||||
|
}
|
||||||
|
function paymentLabel (order) {
|
||||||
|
const map = {
|
||||||
|
WECHAT: '微信支付',
|
||||||
|
ALIPAY: '支付宝',
|
||||||
|
WALLET: '余额支付',
|
||||||
|
BANK_TRANSFER: '线下转账'
|
||||||
|
}
|
||||||
|
if (order.paymentMethod && map[order.paymentMethod]) {
|
||||||
|
return map[order.paymentMethod]
|
||||||
|
}
|
||||||
|
if (order.payStatus === 'UNPAID' || order.orderStatus === 'UNPAID') {
|
||||||
|
return '待支付'
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => route.query.tag, () => {
|
||||||
|
if (props.homePage || route.name !== 'MyOrder') return;
|
||||||
|
const next = route.query.tag || 'ALL';
|
||||||
|
if (params.tag === next) return;
|
||||||
|
applyRouteTag();
|
||||||
|
getList();
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (props.homePage) params.pageSize = 5;
|
||||||
|
if (!props.homePage) applyRouteTag();
|
||||||
|
getList();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -410,7 +410,9 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
|
import { computed, onMounted, reactive, ref } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
import {
|
import {
|
||||||
orderDetail,
|
orderDetail,
|
||||||
@@ -438,276 +440,278 @@ import {
|
|||||||
* 订单详情:E_COUPON 展示 orderItems[].cardKeys,屏蔽售后/物流(FR-B-03 / FR-B-04)。
|
* 订单详情:E_COUPON 展示 orderItems[].cardKeys,屏蔽售后/物流(FR-B-03 / FR-B-04)。
|
||||||
* 移动端已完成订单用 Dialog 查看卡密(原型 P-09 / D-01)。
|
* 移动端已完成订单用 Dialog 查看卡密(原型 P-09 / D-01)。
|
||||||
*/
|
*/
|
||||||
export default {
|
defineOptions({ name: "order-detail" });
|
||||||
name: "order-detail",
|
|
||||||
data() {
|
const route = useRoute();
|
||||||
return {
|
const router = useRouter();
|
||||||
order: {}, // 订单详情数据
|
|
||||||
progressList: [], // 订单流程
|
const order = ref({}); // 订单详情数据
|
||||||
logistics: "", // 物流数据
|
const progressList = ref([]); // 订单流程
|
||||||
cardKeyDialogVisible: false,
|
const logistics = ref(""); // 物流数据
|
||||||
cancelParams: {
|
const cardKeyDialogVisible = ref(false);
|
||||||
// 取消售后参数
|
const cancelParams = reactive({
|
||||||
orderSn: "",
|
// 取消售后参数
|
||||||
reason: "",
|
orderSn: "",
|
||||||
},
|
reason: "",
|
||||||
cancelAvail: false, // 取消订单modal控制
|
});
|
||||||
cancelReason: [], // 取消订单原因
|
const cancelAvail = ref(false); // 取消订单modal控制
|
||||||
orderPackage: [],
|
const cancelReason = ref([]); // 取消订单原因
|
||||||
packageTraceList: [],
|
const orderPackage = ref([]);
|
||||||
logisticsModal: false,
|
const packageTraceList = ref([]);
|
||||||
};
|
const logisticsModal = ref(false);
|
||||||
},
|
|
||||||
computed: {
|
/** 电子卡券订单:无物流/核验码,卡密数据来自 API-B-01 orderItems[].cardKeys */
|
||||||
/** 电子卡券订单:无物流/核验码,卡密数据来自 API-B-01 orderItems[].cardKeys */
|
const isECouponOrder = computed(() => checkECouponOrder(order.value?.order?.orderType));
|
||||||
isECouponOrder() {
|
const isVirtualOrder = computed(() => order.value?.order?.orderType === "VIRTUAL");
|
||||||
return checkECouponOrder(this.order?.order?.orderType);
|
const isNonPhysicalOrder = computed(() => isVirtualOrder.value || isECouponOrder.value);
|
||||||
},
|
/** 是否展示卡密区块(仅当前订单 orderItems) */
|
||||||
isVirtualOrder() {
|
const hasCardKeySection = computed(() =>
|
||||||
return this.order?.order?.orderType === "VIRTUAL";
|
orderHasCardKeySection({
|
||||||
},
|
orderItems: order.value.orderItems,
|
||||||
isNonPhysicalOrder() {
|
isECouponOrder: isECouponOrder.value,
|
||||||
return this.isVirtualOrder || this.isECouponOrder;
|
})
|
||||||
},
|
);
|
||||||
/** 是否展示卡密区块(仅当前订单 orderItems) */
|
const cardKeyFulfillLines = computed(() => collectCardKeyFulfillLines(order.value.orderItems));
|
||||||
hasCardKeySection() {
|
const ecouponCardKeyRows = computed(() => flattenOrderCardKeys(order.value.orderItems, false));
|
||||||
return orderHasCardKeySection({
|
/** 商家以无需配送方式发货(包裹 fulfillmentMode=NO_DELIVERY) */
|
||||||
orderItems: this.order.orderItems,
|
const isNoDeliveryOrder = computed(() => checkNoDeliveryOrder(orderPackage.value));
|
||||||
isECouponOrder: this.isECouponOrder,
|
const displayDeliveryMethodValue = computed(() => {
|
||||||
});
|
if (isNoDeliveryOrder.value) return "无需配送";
|
||||||
},
|
return order.value?.deliveryMethodValue || "-";
|
||||||
cardKeyFulfillLines() {
|
});
|
||||||
return collectCardKeyFulfillLines(this.order.orderItems);
|
const noDeliveryPackages = computed(() => (orderPackage.value || []).filter(isNoDeliveryPackage));
|
||||||
},
|
const noDeliveryRemark = computed(() => {
|
||||||
ecouponCardKeyRows() {
|
const pkg = noDeliveryPackages.value.find((p) => p.deliveryRemark);
|
||||||
return flattenOrderCardKeys(this.order.orderItems, false);
|
return pkg?.deliveryRemark || "";
|
||||||
},
|
});
|
||||||
/** 商家以无需配送方式发货(包裹 fulfillmentMode=NO_DELIVERY) */
|
const noDeliveryShipTime = computed(() => {
|
||||||
isNoDeliveryOrder() {
|
const pkg = noDeliveryPackages.value[0];
|
||||||
return checkNoDeliveryOrder(this.orderPackage);
|
return pkg?.logisticsTime || "";
|
||||||
},
|
});
|
||||||
displayDeliveryMethodValue() {
|
|
||||||
if (this.isNoDeliveryOrder) return "无需配送";
|
function lineCardKeyRows(line) {
|
||||||
return this.order?.deliveryMethodValue || "-";
|
return line.cardKeys || [];
|
||||||
},
|
}
|
||||||
noDeliveryPackages() {
|
|
||||||
return (this.orderPackage || []).filter(isNoDeliveryPackage);
|
function copyCardKey(row) {
|
||||||
},
|
const text = `卡号:${row.cardNo || ""}\n卡密:${row.cardSecret || ""}`;
|
||||||
noDeliveryRemark() {
|
copyText(text);
|
||||||
const pkg = this.noDeliveryPackages.find((p) => p.deliveryRemark);
|
}
|
||||||
return pkg?.deliveryRemark || "";
|
|
||||||
},
|
function copyAllCardKeys() {
|
||||||
noDeliveryShipTime() {
|
const text = ecouponCardKeyRows.value
|
||||||
const pkg = this.noDeliveryPackages[0];
|
.map((row, i) => `${i + 1}. 卡号:${row.cardNo || ""} 卡密:${row.cardSecret || ""}`)
|
||||||
return pkg?.logisticsTime || "";
|
.join("\n");
|
||||||
},
|
copyText(text || "");
|
||||||
},
|
}
|
||||||
methods: {
|
|
||||||
isNoDeliveryPackage,
|
function copyText(text) {
|
||||||
getFulfillmentModeLabel,
|
if (!text) return;
|
||||||
cardKeyFulfillAlertType,
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||||
resolveCardKeyFulfillMessage,
|
navigator.clipboard.writeText(text).then(() => {
|
||||||
lineCardKeyRows(line) {
|
Message.success("已复制到剪贴板");
|
||||||
return line.cardKeys || [];
|
});
|
||||||
},
|
} else {
|
||||||
copyCardKey(row) {
|
const ta = document.createElement("textarea");
|
||||||
const text = `卡号:${row.cardNo || ""}\n卡密:${row.cardSecret || ""}`;
|
ta.value = text;
|
||||||
this.copyText(text);
|
document.body.appendChild(ta);
|
||||||
},
|
ta.select();
|
||||||
copyAllCardKeys() {
|
document.execCommand("copy");
|
||||||
const text = this.ecouponCardKeyRows
|
document.body.removeChild(ta);
|
||||||
.map((row, i) => `${i + 1}. 卡号:${row.cardNo || ""} 卡密:${row.cardSecret || ""}`)
|
Message.success("已复制到剪贴板");
|
||||||
.join("\n");
|
}
|
||||||
this.copyText(text || "");
|
}
|
||||||
},
|
|
||||||
copyText(text) {
|
function isVatSpecialReceipt(receipt) {
|
||||||
if (!text) return;
|
if (!receipt) return false;
|
||||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
const rt = receipt.receiptType != null ? String(receipt.receiptType).trim() : "";
|
||||||
navigator.clipboard.writeText(text).then(() => {
|
if (rt === "2" || rt === "增值税专用发票" || receipt.invoiceKind === "VAT_SPECIAL") {
|
||||||
Message.success("已复制到剪贴板");
|
return true;
|
||||||
});
|
}
|
||||||
} else {
|
return false;
|
||||||
const ta = document.createElement("textarea");
|
}
|
||||||
ta.value = text;
|
|
||||||
document.body.appendChild(ta);
|
function isPersonalReceipt(receipt) {
|
||||||
ta.select();
|
return !isVatSpecialReceipt(receipt);
|
||||||
document.execCommand("copy");
|
}
|
||||||
document.body.removeChild(ta);
|
|
||||||
Message.success("已复制到剪贴板");
|
function formatReceiptType(receipt) {
|
||||||
|
if (!receipt) return "";
|
||||||
|
const rt = receipt.receiptType != null ? String(receipt.receiptType).trim() : "";
|
||||||
|
if (rt === "电子普通发票" || rt === "增值税专用发票") return rt;
|
||||||
|
return isVatSpecialReceipt(receipt) ? "增值税专用发票" : "电子普通发票";
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatReceiptHeaderType(receipt) {
|
||||||
|
if (!receipt) return "";
|
||||||
|
if (isVatSpecialReceipt(receipt)) return "单位";
|
||||||
|
if (receipt.taxpayerId) return "单位";
|
||||||
|
return "个人";
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasValidReceipt() {
|
||||||
|
const receipt = order.value && order.value.receipt;
|
||||||
|
if (!receipt) return false;
|
||||||
|
const content = receipt.receiptContent ? String(receipt.receiptContent).trim() : '';
|
||||||
|
const title = receipt.receiptTitle ? String(receipt.receiptTitle).trim() : '';
|
||||||
|
const taxpayerId = receipt.taxpayerId ? String(receipt.taxpayerId).trim() : '';
|
||||||
|
// 后端有时会返回占位 receipt(如 receiptContent=不开发票),这里过滤掉
|
||||||
|
if (content === '不开发票') return false;
|
||||||
|
return Boolean(content || title || taxpayerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getInvoiceAddress(receipt) {
|
||||||
|
if (!receipt) return "";
|
||||||
|
return receipt.invoiceAddress || receipt.invoiceFileUrl || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function viewReceiptInvoice(receipt) {
|
||||||
|
const invoiceAddress = getInvoiceAddress(receipt);
|
||||||
|
if (!invoiceAddress) {
|
||||||
|
Message.warning("暂无发票附件");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.open(invoiceAddress, "_blank");
|
||||||
|
}
|
||||||
|
|
||||||
|
function goodsDetail(skuId, goodsId) {
|
||||||
|
// 跳转商品详情
|
||||||
|
let routeUrl = router.resolve({
|
||||||
|
path: "/goodsDetail",
|
||||||
|
query: { skuId, goodsId },
|
||||||
|
});
|
||||||
|
window.open(routeUrl.href, "_blank");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳转店铺首页
|
||||||
|
function shopPage(id) {
|
||||||
|
router.push({
|
||||||
|
path: "/merchant",
|
||||||
|
query: { id: id },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getDetail() {
|
||||||
|
// 获取订单详情
|
||||||
|
orderDetail(route.query.sn).then(async (res) => {
|
||||||
|
if (res.success) {
|
||||||
|
order.value = res.result;
|
||||||
|
const receiptId =
|
||||||
|
(res.result.order && res.result.order.receiptId) ||
|
||||||
|
res.result.receiptId ||
|
||||||
|
(res.result.receipt && res.result.receipt.id);
|
||||||
|
if (receiptId) {
|
||||||
|
const receiptRes = await receiptDetail(receiptId);
|
||||||
|
if (receiptRes && receiptRes.success && receiptRes.result) {
|
||||||
|
order.value["receipt"] = receiptRes.result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
progressList.value = (res.result.orderLogs || []).slice().sort((a, b) => {
|
||||||
isVatSpecialReceipt (receipt) {
|
return new Date(a.createTime).getTime() - new Date(b.createTime).getTime();
|
||||||
if (!receipt) return false;
|
});
|
||||||
const rt = receipt.receiptType != null ? String(receipt.receiptType).trim() : "";
|
if (order.value.order.deliveryMethod === 'LOGISTICS') {
|
||||||
if (rt === "2" || rt === "增值税专用发票" || receipt.invoiceKind === "VAT_SPECIAL") {
|
getOrderPackage(order.value.order.sn);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
}
|
||||||
},
|
});
|
||||||
isPersonalReceipt (receipt) {
|
}
|
||||||
return !this.isVatSpecialReceipt(receipt);
|
|
||||||
},
|
function getOrderPackage(sn) {
|
||||||
formatReceiptType (receipt) {
|
getPackage(sn).then(res => {
|
||||||
if (!receipt) return "";
|
if (res.success) {
|
||||||
const rt = receipt.receiptType != null ? String(receipt.receiptType).trim() : "";
|
orderPackage.value = Array.isArray(res.result) ? res.result : [];
|
||||||
if (rt === "电子普通发票" || rt === "增值税专用发票") return rt;
|
if (!orderPackage.value.length) {
|
||||||
return this.isVatSpecialReceipt(receipt) ? "增值税专用发票" : "电子普通发票";
|
traces();
|
||||||
},
|
|
||||||
formatReceiptHeaderType (receipt) {
|
|
||||||
if (!receipt) return "";
|
|
||||||
if (this.isVatSpecialReceipt(receipt)) return "单位";
|
|
||||||
if (receipt.taxpayerId) return "单位";
|
|
||||||
return "个人";
|
|
||||||
},
|
|
||||||
hasValidReceipt () {
|
|
||||||
const receipt = this.order && this.order.receipt;
|
|
||||||
if (!receipt) return false;
|
|
||||||
const content = receipt.receiptContent ? String(receipt.receiptContent).trim() : '';
|
|
||||||
const title = receipt.receiptTitle ? String(receipt.receiptTitle).trim() : '';
|
|
||||||
const taxpayerId = receipt.taxpayerId ? String(receipt.taxpayerId).trim() : '';
|
|
||||||
// 后端有时会返回占位 receipt(如 receiptContent=不开发票),这里过滤掉
|
|
||||||
if (content === '不开发票') return false;
|
|
||||||
return Boolean(content || title || taxpayerId);
|
|
||||||
},
|
|
||||||
getInvoiceAddress (receipt) {
|
|
||||||
if (!receipt) return "";
|
|
||||||
return receipt.invoiceAddress || receipt.invoiceFileUrl || "";
|
|
||||||
},
|
|
||||||
viewReceiptInvoice (receipt) {
|
|
||||||
const invoiceAddress = this.getInvoiceAddress(receipt);
|
|
||||||
if (!invoiceAddress) {
|
|
||||||
Message.warning("暂无发票附件");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
window.open(invoiceAddress, "_blank");
|
} else {
|
||||||
},
|
traces();
|
||||||
goodsDetail(skuId, goodsId) {
|
}
|
||||||
// 跳转商品详情
|
}).catch(() => {
|
||||||
let routeUrl = this.$router.resolve({
|
traces();
|
||||||
path: "/goodsDetail",
|
});
|
||||||
query: { skuId, goodsId },
|
}
|
||||||
});
|
|
||||||
window.open(routeUrl.href, "_blank");
|
function traces() {
|
||||||
},
|
// 物流信息
|
||||||
// 跳转店铺首页
|
getTraces(route.query.sn).then((res) => {
|
||||||
shopPage(id) {
|
if (res.success) {
|
||||||
this.$router.push({
|
logistics.value = res.result;
|
||||||
path: "/merchant",
|
}
|
||||||
query: { id: id },
|
});
|
||||||
});
|
}
|
||||||
},
|
|
||||||
async getDetail() {
|
function logisticsList() {
|
||||||
// 获取订单详情
|
logisticsModal.value = true;
|
||||||
orderDetail(this.$route.query.sn).then(async (res) => {
|
packageTraceList.value = orderPackage.value;
|
||||||
if (res.success) {
|
// getTracesList(this.order.order.sn).then((res) => {
|
||||||
this.order = res.result;
|
// if (res.success && res.result != null) {
|
||||||
const receiptId =
|
// this.packageTraceList = res.result;
|
||||||
(res.result.order && res.result.order.receiptId) ||
|
// }
|
||||||
res.result.receiptId ||
|
// });
|
||||||
(res.result.receipt && res.result.receipt.id);
|
}
|
||||||
if (receiptId) {
|
|
||||||
const receiptRes = await receiptDetail(receiptId);
|
function received(sn) {
|
||||||
if (receiptRes && receiptRes.success && receiptRes.result) {
|
// 确认收货
|
||||||
this.order["receipt"] = receiptRes.result;
|
sureReceived(sn).then((res) => {
|
||||||
}
|
if (res.success) {
|
||||||
}
|
Message.success("确认收货成功");
|
||||||
this.progressList = (res.result.orderLogs || []).slice().sort((a, b) => {
|
getDetail();
|
||||||
return new Date(a.createTime).getTime() - new Date(b.createTime).getTime();
|
}
|
||||||
});
|
});
|
||||||
if (this.order.order.deliveryMethod === 'LOGISTICS') {
|
}
|
||||||
this.getOrderPackage(this.order.order.sn);
|
|
||||||
}
|
function goPay(sn) {
|
||||||
}
|
// 去支付
|
||||||
});
|
router.push({
|
||||||
},
|
path: "/payment",
|
||||||
getOrderPackage(sn) {
|
query: { orderType: "ORDER", sn },
|
||||||
getPackage(sn).then(res => {
|
});
|
||||||
if (res.success) {
|
}
|
||||||
this.orderPackage = Array.isArray(res.result) ? res.result : [];
|
|
||||||
if (!this.orderPackage.length) {
|
function applyAfterSale(sn) {
|
||||||
this.traces();
|
// 申请售后
|
||||||
}
|
router.push({ name: "ApplyAfterSale", query: { sn: sn } });
|
||||||
} else {
|
}
|
||||||
this.traces();
|
|
||||||
}
|
function comment(sn, goodsIndex) {
|
||||||
}).catch(() => {
|
// 评价
|
||||||
this.traces();
|
router.push({
|
||||||
});
|
path: "/home/addEval",
|
||||||
},
|
query: { sn, index: goodsIndex },
|
||||||
traces() {
|
});
|
||||||
// 物流信息
|
}
|
||||||
getTraces(this.$route.query.sn).then((res) => {
|
|
||||||
if (res.success) {
|
function complain(sn, goodsIndex) {
|
||||||
this.logistics = res.result;
|
// 投诉
|
||||||
}
|
router.push({ name: "Complain", query: { sn, index: goodsIndex } });
|
||||||
});
|
}
|
||||||
},
|
|
||||||
logisticsList() {
|
function handleCancelOrder(sn) {
|
||||||
this.logisticsModal = true;
|
// 取消订单
|
||||||
this.packageTraceList = this.orderPackage;
|
cancelParams.orderSn = sn;
|
||||||
// getTracesList(this.order.order.sn).then((res) => {
|
afterSaleReason("CANCEL").then((res) => {
|
||||||
// if (res.success && res.result != null) {
|
if (res.success) {
|
||||||
// this.packageTraceList = res.result;
|
cancelReason.value = res.result;
|
||||||
// }
|
cancelAvail.value = true;
|
||||||
// });
|
cancelParams.reason = cancelReason.value[0].reason;
|
||||||
},
|
}
|
||||||
received(sn) {
|
});
|
||||||
// 确认收货
|
}
|
||||||
sureReceived(sn).then((res) => {
|
|
||||||
if (res.success) {
|
function sureCancel() {
|
||||||
Message.success("确认收货成功");
|
// 确定取消
|
||||||
this.getDetail();
|
cancelOrder(cancelParams).then((res) => {
|
||||||
}
|
if (res.success) {
|
||||||
});
|
Message.success("取消订单成功");
|
||||||
},
|
getDetail();
|
||||||
goPay(sn) {
|
cancelAvail.value = false;
|
||||||
// 去支付
|
}
|
||||||
this.$router.push({
|
});
|
||||||
path: "/payment",
|
}
|
||||||
query: { orderType: "ORDER", sn },
|
|
||||||
});
|
onMounted(() => {
|
||||||
},
|
getDetail();
|
||||||
applyAfterSale(sn) {
|
});
|
||||||
// 申请售后
|
|
||||||
this.$router.push({ name: "ApplyAfterSale", query: { sn: sn } });
|
|
||||||
},
|
|
||||||
comment(sn, goodsIndex) {
|
|
||||||
// 评价
|
|
||||||
this.$router.push({
|
|
||||||
path: "/home/addEval",
|
|
||||||
query: { sn, index: goodsIndex },
|
|
||||||
});
|
|
||||||
},
|
|
||||||
complain(sn, goodsIndex) {
|
|
||||||
// 投诉
|
|
||||||
this.$router.push({ name: "Complain", query: { sn, index: goodsIndex } });
|
|
||||||
},
|
|
||||||
handleCancelOrder(sn) {
|
|
||||||
// 取消订单
|
|
||||||
this.cancelParams.orderSn = sn;
|
|
||||||
afterSaleReason("CANCEL").then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
this.cancelReason = res.result;
|
|
||||||
this.cancelAvail = true;
|
|
||||||
this.cancelParams.reason = this.cancelReason[0].reason;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
sureCancel() {
|
|
||||||
// 确定取消
|
|
||||||
cancelOrder(this.cancelParams).then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
Message.success("取消订单成功");
|
|
||||||
this.getDetail();
|
|
||||||
this.cancelAvail = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.getDetail();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.mb_10 {
|
.mb_10 {
|
||||||
|
|||||||
@@ -50,125 +50,128 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { computed, onMounted, reactive, ref } from "vue";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
import { memberCouponList } from "@/api/member.js";
|
import { memberCouponList } from "@/api/member.js";
|
||||||
export default {
|
|
||||||
name: "Coupons",
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
statusNameList: [
|
|
||||||
// 优惠券状态
|
|
||||||
"未使用",
|
|
||||||
"已使用",
|
|
||||||
"已过期",
|
|
||||||
],
|
|
||||||
statusList: ["NEW", "USED", "EXPIRE"], // 优惠券状态
|
|
||||||
loading: false, // 列表加载状态
|
|
||||||
params: {
|
|
||||||
// 请求参数
|
|
||||||
pageNumber: 1,
|
|
||||||
pageSize: 20,
|
|
||||||
memberCouponStatus: "NEW",
|
|
||||||
},
|
|
||||||
total: 0, // 优惠券总数
|
|
||||||
list: [], // 优惠券列表
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
activeStatusIndex() {
|
|
||||||
const index = this.statusList.indexOf(this.params.memberCouponStatus);
|
|
||||||
return index > -1 ? index : 0;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getList() {
|
|
||||||
// 获取优惠券列表
|
|
||||||
this.loading = true;
|
|
||||||
memberCouponList(this.params)
|
|
||||||
.then((res) => {
|
|
||||||
this.loading = false;
|
|
||||||
if (res.success) {
|
|
||||||
this.list = res.result.records || [];
|
|
||||||
this.total = res.result.total || 0;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
this.loading = false;
|
|
||||||
this.list = [];
|
|
||||||
this.total = 0;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 切换优惠券状态
|
|
||||||
change(index) {
|
|
||||||
this.params.memberCouponStatus = this.statusList[index];
|
|
||||||
this.params.pageNumber = 1;
|
|
||||||
this.getList();
|
|
||||||
},
|
|
||||||
go(item) {
|
|
||||||
// 根据使用条件跳转商品列表页面
|
|
||||||
if (this.params.memberCouponStatus !== "NEW") return;
|
|
||||||
|
|
||||||
this.$router.push({
|
defineOptions({ name: "Coupons" });
|
||||||
path: "/goodsList",
|
|
||||||
query: { promotionsId: item.couponId, promotionType: "COUPON" },
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
changePageNum(val) {
|
const router = useRouter();
|
||||||
// 分页改变页码
|
|
||||||
this.params.pageNumber = val;
|
|
||||||
this.getList();
|
|
||||||
},
|
|
||||||
|
|
||||||
changePageSize(val) {
|
const statusNameList = ref([
|
||||||
// 分页改变页数
|
// 优惠券状态
|
||||||
this.params.pageNumber = 1;
|
"未使用",
|
||||||
this.params.pageSize = val;
|
"已使用",
|
||||||
this.getList();
|
"已过期",
|
||||||
},
|
]);
|
||||||
|
const statusList = ref(["NEW", "USED", "EXPIRE"]); // 优惠券状态
|
||||||
|
const loading = ref(false); // 列表加载状态
|
||||||
|
const params = reactive({
|
||||||
|
// 请求参数
|
||||||
|
pageNumber: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
memberCouponStatus: "NEW",
|
||||||
|
});
|
||||||
|
const total = ref(0); // 优惠券总数
|
||||||
|
const list = ref([]); // 优惠券列表
|
||||||
|
|
||||||
useScope(type, storeName) {
|
const activeStatusIndex = computed(() => {
|
||||||
// 根据字段返回 优惠券适用范围
|
const index = statusList.value.indexOf(params.memberCouponStatus);
|
||||||
let shop = "平台";
|
return index > -1 ? index : 0;
|
||||||
let goods = "全部商品";
|
});
|
||||||
if (storeName !== "platform") shop = storeName;
|
|
||||||
switch (type) {
|
// 获取优惠券列表
|
||||||
case "ALL":
|
function getList() {
|
||||||
goods = "全部商品";
|
loading.value = true;
|
||||||
break;
|
memberCouponList(params)
|
||||||
case "PORTION_GOODS":
|
.then((res) => {
|
||||||
goods = "部分商品";
|
loading.value = false;
|
||||||
break;
|
if (res.success) {
|
||||||
case "PORTION_GOODS_CATEGORY":
|
list.value = res.result.records || [];
|
||||||
goods = "部分分类商品";
|
total.value = res.result.total || 0;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return `${shop}${goods}可用`;
|
})
|
||||||
},
|
.catch(() => {
|
||||||
formatValidity(item) {
|
loading.value = false;
|
||||||
const start = this.formatCouponTime(item && item.startTime);
|
list.value = [];
|
||||||
const end = this.formatCouponTime(item && item.endTime);
|
total.value = 0;
|
||||||
if (start && end) return `${start} 至 ${end}`;
|
});
|
||||||
if (end) return `至 ${end}`;
|
}
|
||||||
if (start) return `${start} 起`;
|
|
||||||
return "长期有效";
|
// 切换优惠券状态
|
||||||
},
|
function change(index) {
|
||||||
formatCouponTime(value) {
|
params.memberCouponStatus = statusList.value[index];
|
||||||
if (!value) return "";
|
params.pageNumber = 1;
|
||||||
const text = String(value).trim();
|
getList();
|
||||||
if (/^\d{4}-\d{2}-\d{2}/.test(text)) {
|
}
|
||||||
return text.slice(0, 16);
|
|
||||||
}
|
// 根据使用条件跳转商品列表页面
|
||||||
const date = new Date(text);
|
function go(item) {
|
||||||
if (Number.isNaN(date.getTime())) return text;
|
if (params.memberCouponStatus !== "NEW") return;
|
||||||
const pad = (n) => String(n).padStart(2, "0");
|
|
||||||
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`;
|
router.push({
|
||||||
},
|
path: "/goodsList",
|
||||||
},
|
query: { promotionsId: item.couponId, promotionType: "COUPON" },
|
||||||
mounted() {
|
});
|
||||||
this.getList();
|
}
|
||||||
},
|
|
||||||
};
|
// 分页改变页码
|
||||||
|
function changePageNum(val) {
|
||||||
|
params.pageNumber = val;
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页改变页数
|
||||||
|
function changePageSize(val) {
|
||||||
|
params.pageNumber = 1;
|
||||||
|
params.pageSize = val;
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据字段返回 优惠券适用范围
|
||||||
|
function useScope(type, storeName) {
|
||||||
|
let shop = "平台";
|
||||||
|
let goods = "全部商品";
|
||||||
|
if (storeName !== "platform") shop = storeName;
|
||||||
|
switch (type) {
|
||||||
|
case "ALL":
|
||||||
|
goods = "全部商品";
|
||||||
|
break;
|
||||||
|
case "PORTION_GOODS":
|
||||||
|
goods = "部分商品";
|
||||||
|
break;
|
||||||
|
case "PORTION_GOODS_CATEGORY":
|
||||||
|
goods = "部分分类商品";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return `${shop}${goods}可用`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatValidity(item) {
|
||||||
|
const start = formatCouponTime(item && item.startTime);
|
||||||
|
const end = formatCouponTime(item && item.endTime);
|
||||||
|
if (start && end) return `${start} 至 ${end}`;
|
||||||
|
if (end) return `至 ${end}`;
|
||||||
|
if (start) return `${start} 起`;
|
||||||
|
return "长期有效";
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatCouponTime(value) {
|
||||||
|
if (!value) return "";
|
||||||
|
const text = String(value).trim();
|
||||||
|
if (/^\d{4}-\d{2}-\d{2}/.test(text)) {
|
||||||
|
return text.slice(0, 16);
|
||||||
|
}
|
||||||
|
const date = new Date(text);
|
||||||
|
if (Number.isNaN(date.getTime())) return text;
|
||||||
|
const pad = (n) => String(n).padStart(2, "0");
|
||||||
|
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getList();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
</p></template>
|
</p></template>
|
||||||
<div>
|
<div>
|
||||||
<el-form
|
<el-form
|
||||||
ref="formData"
|
ref="formDataRef"
|
||||||
label-width="100px"
|
label-width="100px"
|
||||||
:model="formData"
|
:model="formData"
|
||||||
:rules="formValidate"
|
:rules="formValidate"
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
</p></template>
|
</p></template>
|
||||||
<div>
|
<div>
|
||||||
<el-form
|
<el-form
|
||||||
ref="withdrawApplyFormData"
|
ref="withdrawApplyFormDataRef"
|
||||||
label-width="120px"
|
label-width="120px"
|
||||||
:model="withdrawApplyFormData"
|
:model="withdrawApplyFormData"
|
||||||
:rules="withdrawApplyFormValidate"
|
:rules="withdrawApplyFormValidate"
|
||||||
@@ -180,229 +180,230 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { onMounted, reactive, ref } from "vue";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
import {getDepositLog, getMembersWallet, getRecharge, getWithdrawApply, recharge, withdrawalApply} from '@/api/member';
|
import {getDepositLog, getMembersWallet, getRecharge, getWithdrawApply, recharge as apiRecharge, withdrawalApply as apiWithdrawalApply} from '@/api/member';
|
||||||
import {withdrawalSettingVO} from "@/api/pay";
|
import {withdrawalSettingVO} from "@/api/pay";
|
||||||
import { Edit } from '@element-plus/icons-vue';
|
import { Edit } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
export default {
|
defineOptions({ name: 'MoneyManagement' });
|
||||||
name: 'MoneyManagement',
|
|
||||||
components: { Edit },
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
activeWalletTab: "log",
|
|
||||||
frozenDeposit: 0, // 冻结余额
|
|
||||||
memberDeposit: 0, // 余额
|
|
||||||
|
|
||||||
modal: false, // 余额充值
|
const router = useRouter();
|
||||||
withdrawApplyModal: false, // 提现申请
|
|
||||||
formData: {
|
const formDataRef = ref(null);
|
||||||
// 充值金额
|
const withdrawApplyFormDataRef = ref(null);
|
||||||
price: 1
|
|
||||||
},
|
const activeWalletTab = ref("log");
|
||||||
// 提现金额
|
const frozenDeposit = ref(0); // 冻结余额
|
||||||
withdrawApplyFormData: {
|
const memberDeposit = ref(0); // 余额
|
||||||
price: 1,
|
|
||||||
minPrice: 1,
|
const modal = ref(false); // 余额充值
|
||||||
type: '',
|
const withdrawApplyModal = ref(false); // 提现申请
|
||||||
realName: '',
|
const formData = reactive({
|
||||||
connectNumber: '',
|
// 充值金额
|
||||||
},
|
price: 1
|
||||||
// 余额日志
|
});
|
||||||
walletForm: {
|
// 提现金额
|
||||||
// 搜索框初始化对象
|
const withdrawApplyFormData = reactive({
|
||||||
pageNumber: 1,
|
price: 1,
|
||||||
pageSize: 20
|
minPrice: 1,
|
||||||
},
|
type: '',
|
||||||
// 充值记录
|
realName: '',
|
||||||
rechargeForm: {
|
connectNumber: '',
|
||||||
// 搜索框初始化对象
|
});
|
||||||
pageNumber: 1, // 当前页数
|
// 余额日志
|
||||||
pageSize: 20 // 页面大小
|
const walletForm = reactive({
|
||||||
},
|
// 搜索框初始化对象
|
||||||
// 提现记录
|
pageNumber: 1,
|
||||||
withdrawApplyForm: {
|
pageSize: 20
|
||||||
// 搜索框初始化对象
|
});
|
||||||
pageNumber: 1, // 当前页数
|
// 充值记录
|
||||||
pageSize: 20 // 页面大小
|
const rechargeForm = reactive({
|
||||||
},
|
// 搜索框初始化对象
|
||||||
// 提现申请校验
|
pageNumber: 1, // 当前页数
|
||||||
withdrawApplyFormValidate: {
|
pageSize: 20 // 页面大小
|
||||||
price: [
|
});
|
||||||
{required: true, message: '请输入大于0小于9999的合法提现金额'},
|
// 提现记录
|
||||||
{
|
const withdrawApplyForm = reactive({
|
||||||
pattern: /^[1-9]\d{0,3}(\.\d{1,2})?$/,
|
// 搜索框初始化对象
|
||||||
message: '请输入大于0小于9999的合法提现金额',
|
pageNumber: 1, // 当前页数
|
||||||
trigger: 'change'
|
pageSize: 20 // 页面大小
|
||||||
}
|
});
|
||||||
],
|
// 提现申请校验
|
||||||
realName: [
|
const withdrawApplyFormValidate = {
|
||||||
{required: true, message: '请输入真实姓名'},
|
price: [
|
||||||
],
|
{required: true, message: '请输入大于0小于9999的合法提现金额'},
|
||||||
connectNumber: [
|
{
|
||||||
{required: true, message: '请输入第三方登录账号'},
|
pattern: /^[1-9]\d{0,3}(\.\d{1,2})?$/,
|
||||||
],
|
message: '请输入大于0小于9999的合法提现金额',
|
||||||
},
|
trigger: 'change'
|
||||||
formValidate: {
|
}
|
||||||
price: [
|
],
|
||||||
{required: true, message: '请输入大于等于1小于9999的合法充值金额'},
|
realName: [
|
||||||
{
|
{required: true, message: '请输入真实姓名'},
|
||||||
pattern: /^[1-9]\d{0,3}(\.\d{1,2})?$/,
|
],
|
||||||
message: '请输入大于等于1小于9999的合法充值金额',
|
connectNumber: [
|
||||||
trigger: 'change'
|
{required: true, message: '请输入第三方登录账号'},
|
||||||
}
|
],
|
||||||
]
|
};
|
||||||
},
|
const formValidate = {
|
||||||
logColumnsData: {},
|
price: [
|
||||||
rechargeListData: {},
|
{required: true, message: '请输入大于等于1小于9999的合法充值金额'},
|
||||||
withdrawApplyColumnsListData: {}
|
{
|
||||||
};
|
pattern: /^[1-9]\d{0,3}(\.\d{1,2})?$/,
|
||||||
},
|
message: '请输入大于等于1小于9999的合法充值金额',
|
||||||
mounted() {
|
trigger: 'change'
|
||||||
this.init();
|
}
|
||||||
},
|
]
|
||||||
methods: {
|
};
|
||||||
// 初始化数据
|
const logColumnsData = ref({});
|
||||||
init() {
|
const rechargeListData = ref({});
|
||||||
getMembersWallet().then((res) => {
|
const withdrawApplyColumnsListData = ref({});
|
||||||
this.frozenDeposit = res.result.memberFrozenWallet;
|
|
||||||
this.memberDeposit = res.result.memberWallet;
|
onMounted(() => {
|
||||||
});
|
init();
|
||||||
getDepositLog(this.walletForm).then((res) => {
|
});
|
||||||
|
|
||||||
|
// 初始化数据
|
||||||
|
function init() {
|
||||||
|
getMembersWallet().then((res) => {
|
||||||
|
frozenDeposit.value = res.result.memberFrozenWallet;
|
||||||
|
memberDeposit.value = res.result.memberWallet;
|
||||||
|
});
|
||||||
|
getDepositLog(walletForm).then((res) => {
|
||||||
|
if (res.message === 'success') {
|
||||||
|
logColumnsData.value = res.result;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function tabPaneChange(v) {
|
||||||
|
activeWalletTab.value = v;
|
||||||
|
if (v === 'recharge') {
|
||||||
|
getRechargeData();
|
||||||
|
}
|
||||||
|
if (v === 'log') {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
if (v === 'withdrawApply') {
|
||||||
|
getWithdrawApplyData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 充值记录
|
||||||
|
function getRechargeData() {
|
||||||
|
getRecharge(rechargeForm).then((res) => {
|
||||||
|
if (res.message === 'success') {
|
||||||
|
rechargeListData.value = res.result;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 提现记录
|
||||||
|
function getWithdrawApplyData() {
|
||||||
|
getWithdrawApply(withdrawApplyForm).then((res) => {
|
||||||
|
if (res.message === 'success') {
|
||||||
|
withdrawApplyColumnsListData.value = res.result;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function rechargeStatusText (status) {
|
||||||
|
const map = { PAID: '已付款', UNPAID: '未付款', CANCEL: '已取消' }
|
||||||
|
return map[status] || status || '—'
|
||||||
|
}
|
||||||
|
function rechargeWayText (way) {
|
||||||
|
const map = { ALIPAY: '支付宝', WECHAT: '微信', BANK_TRANSFER: '线下转账' }
|
||||||
|
return map[way] || way || '—'
|
||||||
|
}
|
||||||
|
function withdrawStatusText (status) {
|
||||||
|
const map = {
|
||||||
|
APPLY: '申请中',
|
||||||
|
VIA_AUDITING: '审核通过',
|
||||||
|
SUCCESS: '提现成功',
|
||||||
|
ERROR: '提现失败'
|
||||||
|
}
|
||||||
|
return map[status] || '审核拒绝'
|
||||||
|
}
|
||||||
|
// 余额日志
|
||||||
|
function changePage(v) {
|
||||||
|
walletForm.pageNumber = v;
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
function changePageSize(v) {
|
||||||
|
walletForm.pageNumber = 1;
|
||||||
|
walletForm.pageSize = v;
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
// 充值记录
|
||||||
|
function rechargeChangePage(v) {
|
||||||
|
rechargeForm.pageNumber = v;
|
||||||
|
getRechargeData();
|
||||||
|
}
|
||||||
|
function rechargeChangePageSize(v) {
|
||||||
|
rechargeForm.pageNumber = 1;
|
||||||
|
rechargeForm.pageSize = v;
|
||||||
|
getRechargeData();
|
||||||
|
}
|
||||||
|
// 提现记录
|
||||||
|
function withdrawChangePage(v) {
|
||||||
|
withdrawApplyForm.pageNumber = v;
|
||||||
|
getWithdrawApplyData();
|
||||||
|
}
|
||||||
|
function withdrawChangePageSize(v) {
|
||||||
|
withdrawApplyForm.pageNumber = 1;
|
||||||
|
withdrawApplyForm.pageSize = v;
|
||||||
|
getWithdrawApplyData();
|
||||||
|
}
|
||||||
|
// 弹出在线充值框
|
||||||
|
function recharge() {
|
||||||
|
formData.price = 1;
|
||||||
|
modal.value = true;
|
||||||
|
}
|
||||||
|
// 在线充值
|
||||||
|
function rechargePrice() {
|
||||||
|
formDataRef.value.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
apiRecharge(formData).then((res) => {
|
||||||
if (res.message === 'success') {
|
if (res.message === 'success') {
|
||||||
this.logColumnsData = res.result;
|
router.push({
|
||||||
}
|
path: '/payment',
|
||||||
});
|
query: {orderType: 'RECHARGE', sn: res.result.rechargeSn}
|
||||||
},
|
|
||||||
tabPaneChange(v) {
|
|
||||||
this.activeWalletTab = v;
|
|
||||||
if (v === 'recharge') {
|
|
||||||
this.getRechargeData();
|
|
||||||
}
|
|
||||||
if (v === 'log') {
|
|
||||||
this.init();
|
|
||||||
}
|
|
||||||
if (v === 'withdrawApply') {
|
|
||||||
this.getWithdrawApplyData();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 充值记录
|
|
||||||
getRechargeData() {
|
|
||||||
getRecharge(this.rechargeForm).then((res) => {
|
|
||||||
if (res.message === 'success') {
|
|
||||||
this.rechargeListData = res.result;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 提现记录
|
|
||||||
getWithdrawApplyData() {
|
|
||||||
getWithdrawApply(this.withdrawApplyForm).then((res) => {
|
|
||||||
if (res.message === 'success') {
|
|
||||||
this.withdrawApplyColumnsListData = res.result;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
rechargeStatusText (status) {
|
|
||||||
const map = { PAID: '已付款', UNPAID: '未付款', CANCEL: '已取消' }
|
|
||||||
return map[status] || status || '—'
|
|
||||||
},
|
|
||||||
rechargeWayText (way) {
|
|
||||||
const map = { ALIPAY: '支付宝', WECHAT: '微信', BANK_TRANSFER: '线下转账' }
|
|
||||||
return map[way] || way || '—'
|
|
||||||
},
|
|
||||||
withdrawStatusText (status) {
|
|
||||||
const map = {
|
|
||||||
APPLY: '申请中',
|
|
||||||
VIA_AUDITING: '审核通过',
|
|
||||||
SUCCESS: '提现成功',
|
|
||||||
ERROR: '提现失败'
|
|
||||||
}
|
|
||||||
return map[status] || '审核拒绝'
|
|
||||||
},
|
|
||||||
// 余额日志
|
|
||||||
changePage(v) {
|
|
||||||
this.walletForm.pageNumber = v;
|
|
||||||
this.init();
|
|
||||||
},
|
|
||||||
changePageSize(v) {
|
|
||||||
this.walletForm.pageNumber = 1;
|
|
||||||
this.walletForm.pageSize = v;
|
|
||||||
this.init();
|
|
||||||
},
|
|
||||||
// 充值记录
|
|
||||||
rechargeChangePage(v) {
|
|
||||||
this.rechargeForm.pageNumber = v;
|
|
||||||
this.getRechargeData();
|
|
||||||
},
|
|
||||||
rechargeChangePageSize(v) {
|
|
||||||
this.rechargeForm.pageNumber = 1;
|
|
||||||
this.rechargeForm.pageSize = v;
|
|
||||||
this.getRechargeData();
|
|
||||||
},
|
|
||||||
// 提现记录
|
|
||||||
withdrawChangePage(v) {
|
|
||||||
this.withdrawApplyForm.pageNumber = v;
|
|
||||||
this.getWithdrawApplyData();
|
|
||||||
},
|
|
||||||
withdrawChangePageSize(v) {
|
|
||||||
this.withdrawApplyForm.pageNumber = 1;
|
|
||||||
this.withdrawApplyForm.pageSize = v;
|
|
||||||
this.getWithdrawApplyData();
|
|
||||||
},
|
|
||||||
// 弹出在线充值框
|
|
||||||
recharge() {
|
|
||||||
this.formData.price = 1;
|
|
||||||
this.modal = true;
|
|
||||||
},
|
|
||||||
// 在线充值
|
|
||||||
rechargePrice() {
|
|
||||||
this.$refs['formData'].validate((valid) => {
|
|
||||||
if (valid) {
|
|
||||||
recharge(this.formData).then((res) => {
|
|
||||||
if (res.message === 'success') {
|
|
||||||
this.$router.push({
|
|
||||||
path: '/payment',
|
|
||||||
query: {orderType: 'RECHARGE', sn: res.result.rechargeSn}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 申请提现弹出框
|
|
||||||
withdrawalApply() {
|
|
||||||
this.withdrawApplyModal = true;
|
|
||||||
this.withdrawApplyFormData.minPrice = 1;
|
|
||||||
this.withdrawApplyFormData.price = 1;
|
|
||||||
this.withdrawApplyFormData.type = '';
|
|
||||||
this.withdrawApplyFormData.realName = '';
|
|
||||||
this.withdrawApplyFormData.connectNumber = '';
|
|
||||||
withdrawalSettingVO().then((res) => {
|
|
||||||
if (res.code === 200) {
|
|
||||||
this.withdrawApplyFormData.minPrice = res.result.minPrice;
|
|
||||||
this.withdrawApplyFormData.type = res.result.type;
|
|
||||||
this.withdrawApplyFormData.price = 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 提现
|
|
||||||
withdrawal() {
|
|
||||||
this.$refs['withdrawApplyFormData'].validate((valid) => {
|
|
||||||
if (valid) {
|
|
||||||
withdrawalApply(this.withdrawApplyFormData).then((res) => {
|
|
||||||
if (res && res.success) {
|
|
||||||
Message.success('提现申请成功,关注提现状态');
|
|
||||||
this.withdrawApplyModal = false;
|
|
||||||
this.init(); // 余额查询
|
|
||||||
this.getWithdrawApplyData(); // 提现记录
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
};
|
}
|
||||||
|
// 申请提现弹出框
|
||||||
|
function withdrawalApply() {
|
||||||
|
withdrawApplyModal.value = true;
|
||||||
|
withdrawApplyFormData.minPrice = 1;
|
||||||
|
withdrawApplyFormData.price = 1;
|
||||||
|
withdrawApplyFormData.type = '';
|
||||||
|
withdrawApplyFormData.realName = '';
|
||||||
|
withdrawApplyFormData.connectNumber = '';
|
||||||
|
withdrawalSettingVO().then((res) => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
withdrawApplyFormData.minPrice = res.result.minPrice;
|
||||||
|
withdrawApplyFormData.type = res.result.type;
|
||||||
|
withdrawApplyFormData.price = 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 提现
|
||||||
|
function withdrawal() {
|
||||||
|
withdrawApplyFormDataRef.value.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
apiWithdrawalApply(withdrawApplyFormData).then((res) => {
|
||||||
|
if (res && res.success) {
|
||||||
|
Message.success('提现申请成功,关注提现状态');
|
||||||
|
withdrawApplyModal.value = false;
|
||||||
|
init(); // 余额查询
|
||||||
|
getWithdrawApplyData(); // 提现记录
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<div class="bind-section mb_20">
|
<div class="bind-section mb_20">
|
||||||
<div class="section-title">兑换礼品卡</div>
|
<div class="section-title">兑换礼品卡</div>
|
||||||
<el-form ref="bindForm" :model="bindForm" :rules="bindRules" inline class="bind-form">
|
<el-form ref="bindFormRef" :model="bindForm" :rules="bindRules" inline class="bind-form">
|
||||||
<el-form-item label="卡号" prop="cardNo" label-width="60px">
|
<el-form-item label="卡号" prop="cardNo" label-width="60px">
|
||||||
<el-input v-model="bindForm.cardNo" placeholder="请输入卡号" maxlength="64" style="width: 220px" />
|
<el-input v-model="bindForm.cardNo" placeholder="请输入卡号" maxlength="64" style="width: 220px" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -92,7 +92,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { computed, nextTick, onMounted, reactive, ref } from "vue";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
import {
|
import {
|
||||||
getGiftCardCashMemberCardPage,
|
getGiftCardCashMemberCardPage,
|
||||||
@@ -100,194 +102,201 @@ import {
|
|||||||
activateGiftCardCashMemberCard,
|
activateGiftCardCashMemberCard,
|
||||||
} from "@/api/promotion.js";
|
} from "@/api/promotion.js";
|
||||||
|
|
||||||
export default {
|
defineOptions({ name: "MyGiftCards" });
|
||||||
name: "MyGiftCards",
|
|
||||||
data() {
|
const router = useRouter();
|
||||||
return {
|
|
||||||
statusTab: "AVAILABLE",
|
const statusTab = ref("AVAILABLE");
|
||||||
loading: false,
|
const loading = ref(false);
|
||||||
total: 0,
|
const total = ref(0);
|
||||||
list: [],
|
const list = ref([]);
|
||||||
params: {
|
const params = reactive({
|
||||||
pageNumber: 1,
|
pageNumber: 1,
|
||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
sort: "createTime",
|
sort: "createTime",
|
||||||
order: "desc",
|
order: "desc",
|
||||||
memberCardStatus: "AVAILABLE",
|
memberCardStatus: "AVAILABLE",
|
||||||
},
|
});
|
||||||
bindSubmitting: false,
|
const bindSubmitting = ref(false);
|
||||||
bindForm: {
|
const bindForm = ref({
|
||||||
cardNo: "",
|
cardNo: "",
|
||||||
cardSecret: "",
|
cardSecret: "",
|
||||||
},
|
});
|
||||||
bindRules: {
|
const bindRules = {
|
||||||
cardNo: [{ required: true, message: "请输入卡号", trigger: "blur" }],
|
cardNo: [{ required: true, message: "请输入卡号", trigger: "blur" }],
|
||||||
cardSecret: [{ required: true, message: "请输入兑换码", trigger: "blur" }],
|
cardSecret: [{ required: true, message: "请输入兑换码", trigger: "blur" }],
|
||||||
},
|
|
||||||
noticeModal: false,
|
|
||||||
serviceHotline: "12221111",
|
|
||||||
activatingId: "",
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
primaryActionLabel() {
|
|
||||||
if (this.statusTab === "PENDING_ACTIVATION") {
|
|
||||||
return "激活自用";
|
|
||||||
}
|
|
||||||
if (this.statusTab === "AVAILABLE") {
|
|
||||||
return "去使用";
|
|
||||||
}
|
|
||||||
return "不可用";
|
|
||||||
},
|
|
||||||
primaryActionDisabled() {
|
|
||||||
return this.statusTab === "UNAVAILABLE";
|
|
||||||
},
|
|
||||||
pendingActivateLocked() {
|
|
||||||
return this.statusTab === "PENDING_ACTIVATION" && !!this.activatingId;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.getList();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
formatYuan(val) {
|
|
||||||
if (val == null || val === "") {
|
|
||||||
return "—";
|
|
||||||
}
|
|
||||||
const n = Number(val);
|
|
||||||
if (Number.isNaN(n)) {
|
|
||||||
return String(val);
|
|
||||||
}
|
|
||||||
return Number.isInteger(n) ? String(n) : n.toFixed(2).replace(/\.?0+$/, "");
|
|
||||||
},
|
|
||||||
formatValidity(row) {
|
|
||||||
const t = row && row.expireTime;
|
|
||||||
if (!t) {
|
|
||||||
return "长期有效";
|
|
||||||
}
|
|
||||||
const d = new Date(t);
|
|
||||||
if (Number.isNaN(d.getTime())) {
|
|
||||||
return "长期有效";
|
|
||||||
}
|
|
||||||
if (d.getFullYear() >= 2099) {
|
|
||||||
return "长期有效";
|
|
||||||
}
|
|
||||||
const y = d.getFullYear();
|
|
||||||
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
||||||
const day = String(d.getDate()).padStart(2, "0");
|
|
||||||
return `${y}-${m}-${day} 到期`;
|
|
||||||
},
|
|
||||||
onStatusTab(name) {
|
|
||||||
this.statusTab = name;
|
|
||||||
this.params.memberCardStatus = name;
|
|
||||||
this.params.pageNumber = 1;
|
|
||||||
this.getList();
|
|
||||||
},
|
|
||||||
getList() {
|
|
||||||
this.loading = true;
|
|
||||||
const p = { ...this.params };
|
|
||||||
if (p.memberCardStatus) {
|
|
||||||
p.memberCardStatus = String(p.memberCardStatus);
|
|
||||||
}
|
|
||||||
getGiftCardCashMemberCardPage(p)
|
|
||||||
.then((res) => {
|
|
||||||
this.loading = false;
|
|
||||||
if (res.success && res.result) {
|
|
||||||
this.list = res.result.records || [];
|
|
||||||
this.total = res.result.total || 0;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
this.loading = false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
changePage(val) {
|
|
||||||
this.params.pageNumber = val;
|
|
||||||
this.getList();
|
|
||||||
},
|
|
||||||
changePageSize(val) {
|
|
||||||
this.params.pageNumber = 1;
|
|
||||||
this.params.pageSize = val;
|
|
||||||
this.getList();
|
|
||||||
},
|
|
||||||
resetBindForm() {
|
|
||||||
this.bindForm = { cardNo: "", cardSecret: "" };
|
|
||||||
this.$nextTick(() => {
|
|
||||||
if (this.$refs.bindForm) {
|
|
||||||
this.$refs.bindForm.resetFields();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
submitBind() {
|
|
||||||
this.$refs.bindForm.validate((valid) => {
|
|
||||||
if (!valid) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.bindSubmitting = true;
|
|
||||||
bindGiftCardCashMemberCard({
|
|
||||||
cardNo: String(this.bindForm.cardNo).trim(),
|
|
||||||
cardSecret: String(this.bindForm.cardSecret).trim(),
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
this.bindSubmitting = false;
|
|
||||||
if (res.success) {
|
|
||||||
Message.success(res.message || "兑换成功");
|
|
||||||
this.resetBindForm();
|
|
||||||
this.params.pageNumber = 1;
|
|
||||||
this.getList();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
this.bindSubmitting = false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onCardPrimaryAction(item) {
|
|
||||||
if (this.primaryActionDisabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.statusTab === "PENDING_ACTIVATION") {
|
|
||||||
const memberCardId = this.resolveMemberCardId(item);
|
|
||||||
if (!memberCardId) {
|
|
||||||
Message.error("卡片信息异常,无法激活");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.activatingId = memberCardId;
|
|
||||||
activateGiftCardCashMemberCard(memberCardId)
|
|
||||||
.then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
Message.success(res.message || "激活成功");
|
|
||||||
this.getList();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.warn('激活礼品卡失败', err);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.activatingId = "";
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.statusTab === "AVAILABLE") {
|
|
||||||
this.$router.push({ path: "/goodsList" });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
resolveMemberCardId(row) {
|
|
||||||
if (!row) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return row.memberCardId != null && row.memberCardId !== ""
|
|
||||||
? String(row.memberCardId)
|
|
||||||
: row.id != null && row.id !== ""
|
|
||||||
? String(row.id)
|
|
||||||
: "";
|
|
||||||
},
|
|
||||||
isActivating(item) {
|
|
||||||
const id = this.resolveMemberCardId(item);
|
|
||||||
return !!this.activatingId && this.activatingId === id;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
const noticeModal = ref(false);
|
||||||
|
const serviceHotline = ref("12221111");
|
||||||
|
const activatingId = ref("");
|
||||||
|
const bindFormRef = ref(null);
|
||||||
|
|
||||||
|
const primaryActionLabel = computed(() => {
|
||||||
|
if (statusTab.value === "PENDING_ACTIVATION") {
|
||||||
|
return "激活自用";
|
||||||
|
}
|
||||||
|
if (statusTab.value === "AVAILABLE") {
|
||||||
|
return "去使用";
|
||||||
|
}
|
||||||
|
return "不可用";
|
||||||
|
});
|
||||||
|
const primaryActionDisabled = computed(() => {
|
||||||
|
return statusTab.value === "UNAVAILABLE";
|
||||||
|
});
|
||||||
|
const pendingActivateLocked = computed(() => {
|
||||||
|
return statusTab.value === "PENDING_ACTIVATION" && !!activatingId.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getList();
|
||||||
|
});
|
||||||
|
|
||||||
|
function formatYuan(val) {
|
||||||
|
if (val == null || val === "") {
|
||||||
|
return "—";
|
||||||
|
}
|
||||||
|
const n = Number(val);
|
||||||
|
if (Number.isNaN(n)) {
|
||||||
|
return String(val);
|
||||||
|
}
|
||||||
|
return Number.isInteger(n) ? String(n) : n.toFixed(2).replace(/\.?0+$/, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatValidity(row) {
|
||||||
|
const t = row && row.expireTime;
|
||||||
|
if (!t) {
|
||||||
|
return "长期有效";
|
||||||
|
}
|
||||||
|
const d = new Date(t);
|
||||||
|
if (Number.isNaN(d.getTime())) {
|
||||||
|
return "长期有效";
|
||||||
|
}
|
||||||
|
if (d.getFullYear() >= 2099) {
|
||||||
|
return "长期有效";
|
||||||
|
}
|
||||||
|
const y = d.getFullYear();
|
||||||
|
const m = String(d.getMonth() + 1).padStart(2, "0");
|
||||||
|
const day = String(d.getDate()).padStart(2, "0");
|
||||||
|
return `${y}-${m}-${day} 到期`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onStatusTab(name) {
|
||||||
|
statusTab.value = name;
|
||||||
|
params.memberCardStatus = name;
|
||||||
|
params.pageNumber = 1;
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getList() {
|
||||||
|
loading.value = true;
|
||||||
|
const p = { ...params };
|
||||||
|
if (p.memberCardStatus) {
|
||||||
|
p.memberCardStatus = String(p.memberCardStatus);
|
||||||
|
}
|
||||||
|
getGiftCardCashMemberCardPage(p)
|
||||||
|
.then((res) => {
|
||||||
|
loading.value = false;
|
||||||
|
if (res.success && res.result) {
|
||||||
|
list.value = res.result.records || [];
|
||||||
|
total.value = res.result.total || 0;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function changePage(val) {
|
||||||
|
params.pageNumber = val;
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
|
||||||
|
function changePageSize(val) {
|
||||||
|
params.pageNumber = 1;
|
||||||
|
params.pageSize = val;
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetBindForm() {
|
||||||
|
bindForm.value = { cardNo: "", cardSecret: "" };
|
||||||
|
nextTick(() => {
|
||||||
|
if (bindFormRef.value) {
|
||||||
|
bindFormRef.value.resetFields();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitBind() {
|
||||||
|
bindFormRef.value.validate((valid) => {
|
||||||
|
if (!valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bindSubmitting.value = true;
|
||||||
|
bindGiftCardCashMemberCard({
|
||||||
|
cardNo: String(bindForm.value.cardNo).trim(),
|
||||||
|
cardSecret: String(bindForm.value.cardSecret).trim(),
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
bindSubmitting.value = false;
|
||||||
|
if (res.success) {
|
||||||
|
Message.success(res.message || "兑换成功");
|
||||||
|
resetBindForm();
|
||||||
|
params.pageNumber = 1;
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
bindSubmitting.value = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCardPrimaryAction(item) {
|
||||||
|
if (primaryActionDisabled.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (statusTab.value === "PENDING_ACTIVATION") {
|
||||||
|
const memberCardId = resolveMemberCardId(item);
|
||||||
|
if (!memberCardId) {
|
||||||
|
Message.error("卡片信息异常,无法激活");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
activatingId.value = memberCardId;
|
||||||
|
activateGiftCardCashMemberCard(memberCardId)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
Message.success(res.message || "激活成功");
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.warn('激活礼品卡失败', err);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
activatingId.value = "";
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (statusTab.value === "AVAILABLE") {
|
||||||
|
router.push({ path: "/goodsList" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveMemberCardId(row) {
|
||||||
|
if (!row) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return row.memberCardId != null && row.memberCardId !== ""
|
||||||
|
? String(row.memberCardId)
|
||||||
|
: row.id != null && row.id !== ""
|
||||||
|
? String(row.id)
|
||||||
|
: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function isActivating(item) {
|
||||||
|
const id = resolveMemberCardId(item);
|
||||||
|
return !!activatingId.value && activatingId.value === id;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -38,98 +38,103 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { onMounted, reactive, ref } from "vue";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
import { Message, Modal } from "@/utils/message";
|
import { Message, Modal } from "@/utils/message";
|
||||||
import { Delete } from '@element-plus/icons-vue';
|
import { Delete } from '@element-plus/icons-vue';
|
||||||
import { tracksList, clearTracks, clearTracksById } from "@/api/member";
|
import { tracksList, clearTracks, clearTracksById } from "@/api/member";
|
||||||
export default {
|
|
||||||
components: { Delete },
|
defineOptions({ name: "MyTrack" });
|
||||||
name: "MyTrack",
|
|
||||||
data() {
|
const router = useRouter();
|
||||||
return {
|
|
||||||
list: [], // 我的足迹,商品列表
|
const list = ref([]); // 我的足迹,商品列表
|
||||||
spinShow: false, // 控制loading是否加载
|
const spinShow = ref(false); // 控制loading是否加载
|
||||||
params: {
|
const params = reactive({
|
||||||
pageNumber: 1,
|
pageNumber: 1,
|
||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
order: "desc",
|
order: "desc",
|
||||||
sort: "updateTime",
|
sort: "updateTime",
|
||||||
},
|
});
|
||||||
total: 0,
|
const total = ref(0);
|
||||||
};
|
|
||||||
},
|
function goodsDetail(skuId, goodsId) {
|
||||||
mounted() {
|
// 跳转商品详情
|
||||||
this.getList();
|
let routeUrl = router.resolve({
|
||||||
},
|
path: "/goodsDetail",
|
||||||
methods: {
|
query: { skuId, goodsId },
|
||||||
goodsDetail(skuId, goodsId) {
|
});
|
||||||
// 跳转商品详情
|
window.open(routeUrl.href, "_blank");
|
||||||
let routeUrl = this.$router.resolve({
|
}
|
||||||
path: "/goodsDetail",
|
|
||||||
query: { skuId, goodsId },
|
// 跳转店铺首页
|
||||||
});
|
function shopPage(id) {
|
||||||
window.open(routeUrl.href, "_blank");
|
router.push({
|
||||||
},
|
path: "/merchant",
|
||||||
// 跳转店铺首页
|
query: { id: id },
|
||||||
shopPage(id) {
|
});
|
||||||
this.$router.push({
|
}
|
||||||
path: "/merchant",
|
|
||||||
query: { id: id },
|
function clearAll() {
|
||||||
});
|
// 清除全部足迹
|
||||||
},
|
Modal.confirm({
|
||||||
clearAll() {
|
title: "删除",
|
||||||
// 清除全部足迹
|
content: "<p>确定要删除全部足迹吗?</p>",
|
||||||
Modal.confirm({
|
onOk: () => {
|
||||||
title: "删除",
|
clearTracks().then((res) => {
|
||||||
content: "<p>确定要删除全部足迹吗?</p>",
|
|
||||||
onOk: () => {
|
|
||||||
clearTracks().then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
Message.success("删除成功");
|
|
||||||
this.getList();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onCancel: () => {},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
clearById(id) {
|
|
||||||
// 清除全部足迹
|
|
||||||
clearTracksById(id).then((res) => {
|
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
Message.success("删除成功");
|
Message.success("删除成功");
|
||||||
this.getList();
|
getList();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
changePageNum(val) {
|
onCancel: () => {},
|
||||||
// 修改页码
|
});
|
||||||
this.params.pageNumber = val;
|
}
|
||||||
this.getList();
|
|
||||||
},
|
function clearById(id) {
|
||||||
changePageSize(val) {
|
// 清除全部足迹
|
||||||
// 修改页数
|
clearTracksById(id).then((res) => {
|
||||||
this.params.pageNumber = 1;
|
if (res.success) {
|
||||||
this.params.pageSize = val;
|
Message.success("删除成功");
|
||||||
this.getList();
|
getList();
|
||||||
},
|
}
|
||||||
getList() {
|
});
|
||||||
// 获取足迹列表
|
}
|
||||||
this.spinShow = true;
|
|
||||||
tracksList(this.params).then((res) => {
|
function changePageNum(val) {
|
||||||
this.spinShow = false;
|
// 修改页码
|
||||||
if (res.success && res.result.records.length) {
|
params.pageNumber = val;
|
||||||
this.list = res.result.records.filter(item =>{
|
getList();
|
||||||
return item != null
|
}
|
||||||
});
|
|
||||||
this.total = res.result.total
|
function changePageSize(val) {
|
||||||
} else {
|
// 修改页数
|
||||||
this.list = [];
|
params.pageNumber = 1;
|
||||||
}
|
params.pageSize = val;
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getList() {
|
||||||
|
// 获取足迹列表
|
||||||
|
spinShow.value = true;
|
||||||
|
tracksList(params).then((res) => {
|
||||||
|
spinShow.value = false;
|
||||||
|
if (res.success && res.result.records.length) {
|
||||||
|
list.value = res.result.records.filter(item =>{
|
||||||
|
return item != null
|
||||||
});
|
});
|
||||||
},
|
total.value = res.result.total
|
||||||
},
|
} else {
|
||||||
};
|
list.value = [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getList();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.wrapper {
|
.wrapper {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -12,13 +12,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
import { onMounted } from "vue";
|
||||||
name: 'PayDone',
|
|
||||||
mounted () {
|
defineOptions({ name: 'PayDone' });
|
||||||
document.querySelector('.pay-done').style.height = window.innerHeight + 'px'
|
|
||||||
}
|
onMounted(() => {
|
||||||
};
|
document.querySelector('.pay-done').style.height = window.innerHeight + 'px'
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang='scss'>
|
<style scoped lang='scss'>
|
||||||
|
|||||||
@@ -35,109 +35,108 @@
|
|||||||
<BaseFooter></BaseFooter>
|
<BaseFooter></BaseFooter>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
|
import { computed, onBeforeUnmount, onMounted, ref } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { tradeDetail, pay } from '@/api/pay.js';
|
import { tradeDetail, pay } from '@/api/pay.js';
|
||||||
import { Message, Modal } from "@/utils/message";
|
import { Message, Modal } from "@/utils/message";
|
||||||
|
|
||||||
export default {
|
const route = useRoute();
|
||||||
data () {
|
const router = useRouter();
|
||||||
return {
|
|
||||||
payDetail: {},
|
const payDetail = ref({});
|
||||||
support: [],
|
const support = ref([]);
|
||||||
walletValue: 0,
|
const walletValue = ref(0);
|
||||||
qrcode: '',
|
const qrcode = ref('');
|
||||||
endTime: 0,
|
const endTime = ref(0);
|
||||||
endText: '订单已超时取消',
|
const endText = ref('订单已超时取消');
|
||||||
isStart: false,
|
const isStart = ref(false);
|
||||||
now: Date.now(),
|
const now = ref(Date.now());
|
||||||
countdownTimer: null,
|
let countdownTimer = null;
|
||||||
};
|
|
||||||
},
|
const countdownText = computed(() => {
|
||||||
computed: {
|
if (!isStart.value || !endTime.value) return '';
|
||||||
countdownText () {
|
const diff = endTime.value - now.value;
|
||||||
if (!this.isStart || !this.endTime) return '';
|
if (diff <= 0) return endText.value;
|
||||||
const diff = this.endTime - this.now;
|
const days = Math.floor(diff / 86400000);
|
||||||
if (diff <= 0) return this.endText;
|
const hours = Math.floor((diff % 86400000) / 3600000);
|
||||||
const days = Math.floor(diff / 86400000);
|
const minutes = Math.floor((diff % 3600000) / 60000);
|
||||||
const hours = Math.floor((diff % 86400000) / 3600000);
|
const seconds = Math.floor((diff % 60000) / 1000);
|
||||||
const minutes = Math.floor((diff % 3600000) / 60000);
|
return `${days}天${hours}小时${minutes}分钟${seconds}秒`;
|
||||||
const seconds = Math.floor((diff % 60000) / 1000);
|
});
|
||||||
return `${days}天${hours}小时${minutes}分钟${seconds}秒`;
|
|
||||||
},
|
// 获取订单详情
|
||||||
},
|
function getTradeDetail() {
|
||||||
methods: {
|
const params = route.query;
|
||||||
// 获取订单详情
|
params.clientType = 'PC'
|
||||||
getTradeDetail () {
|
tradeDetail(params).then(res => {
|
||||||
const params = this.$route.query;
|
if (res.success) {
|
||||||
params.clientType = 'PC'
|
payDetail.value = res.result;
|
||||||
tradeDetail(params).then(res => {
|
endTime.value = payDetail.value.autoCancel
|
||||||
if (res.success) {
|
isStart.value = true
|
||||||
this.payDetail = res.result;
|
support.value = payDetail.value.support
|
||||||
this.endTime = this.payDetail.autoCancel
|
walletValue.value = payDetail.value.walletValue
|
||||||
this.isStart = true
|
const price = Number(payDetail.value.price);
|
||||||
this.support = this.payDetail.support
|
if (!Number.isNaN(price) && price <= 0) {
|
||||||
this.walletValue = this.payDetail.walletValue
|
router.replace('/payDone');
|
||||||
const price = Number(this.payDetail.price);
|
|
||||||
if (!Number.isNaN(price) && price <= 0) {
|
|
||||||
this.$router.replace('/payDone');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).catch((err) => {
|
|
||||||
this.$router.push({name: 'MyOrder'});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 支付
|
|
||||||
handlePay (way) {
|
|
||||||
// 余额支付则直接跳转
|
|
||||||
if (way === 'WALLET') {
|
|
||||||
// 如果待支付金额大于余额,则报错
|
|
||||||
if (this.payDetail.price > this.walletValue) {
|
|
||||||
Message.error('余额不足以支付当前订单,如需充值请前往会员中心');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const params = this.$route.query;
|
|
||||||
params.paymentMethod = way;
|
|
||||||
params.paymentClient = 'NATIVE';
|
|
||||||
params.price = this.payDetail.price;
|
|
||||||
if (way === 'WALLET') {
|
|
||||||
Modal.confirm({
|
|
||||||
title: '支付确认',
|
|
||||||
content: '确认使用余额支付吗?',
|
|
||||||
onOk: () => {
|
|
||||||
return pay(params)
|
|
||||||
.then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
Message.success(res.message || '支付成功');
|
|
||||||
this.$router.push('/payDone');
|
|
||||||
} else {
|
|
||||||
Message.warning(res.message || '支付失败');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
// 支付失败兜底:业务错误(如 CARD_KEY_STORE_SELL_FORBIDDEN)通常已由 axios 拦截器提示
|
|
||||||
if (err?.message && !err?.data?.message) {
|
|
||||||
Message.error(err.message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.$router.push({path: '/qrpay', query: params});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}).catch((err) => {
|
||||||
mounted () {
|
router.push({name: 'MyOrder'});
|
||||||
this.getTradeDetail();
|
});
|
||||||
this.countdownTimer = setInterval(() => {
|
}
|
||||||
this.now = Date.now();
|
|
||||||
}, 1000);
|
// 支付
|
||||||
},
|
function handlePay(way) {
|
||||||
beforeUnmount () {
|
// 余额支付则直接跳转
|
||||||
if (this.countdownTimer) clearInterval(this.countdownTimer);
|
if (way === 'WALLET') {
|
||||||
},
|
// 如果待支付金额大于余额,则报错
|
||||||
};
|
if (payDetail.value.price > walletValue.value) {
|
||||||
|
Message.error('余额不足以支付当前订单,如需充值请前往会员中心');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const params = route.query;
|
||||||
|
params.paymentMethod = way;
|
||||||
|
params.paymentClient = 'NATIVE';
|
||||||
|
params.price = payDetail.value.price;
|
||||||
|
if (way === 'WALLET') {
|
||||||
|
Modal.confirm({
|
||||||
|
title: '支付确认',
|
||||||
|
content: '确认使用余额支付吗?',
|
||||||
|
onOk: () => {
|
||||||
|
return pay(params)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
Message.success(res.message || '支付成功');
|
||||||
|
router.push('/payDone');
|
||||||
|
} else {
|
||||||
|
Message.warning(res.message || '支付失败');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
// 支付失败兜底:业务错误(如 CARD_KEY_STORE_SELL_FORBIDDEN)通常已由 axios 拦截器提示
|
||||||
|
if (err?.message && !err?.data?.message) {
|
||||||
|
Message.error(err.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
router.push({path: '/qrpay', query: params});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getTradeDetail();
|
||||||
|
countdownTimer = setInterval(() => {
|
||||||
|
now.value = Date.now();
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (countdownTimer) clearInterval(countdownTimer);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.head-left {
|
.head-left {
|
||||||
|
|||||||
@@ -44,89 +44,91 @@
|
|||||||
<BaseFooter></BaseFooter>
|
<BaseFooter></BaseFooter>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
|
import { computed, onBeforeUnmount, onMounted, ref } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
import { Iphone } from '@element-plus/icons-vue';
|
import { Iphone } from '@element-plus/icons-vue';
|
||||||
import vueQr from 'vue-qr';
|
import vueQr from 'vue-qr';
|
||||||
import { payCallback, pay } from '@/api/pay.js';
|
import { payCallback, pay } from '@/api/pay.js';
|
||||||
export default {
|
|
||||||
components: { vueQr, Iphone },
|
const route = useRoute();
|
||||||
data () {
|
const router = useRouter();
|
||||||
return {
|
|
||||||
qrcode: '',
|
const qrcode = ref('');
|
||||||
params: this.$route.query,
|
const params = ref(route.query);
|
||||||
interval: null,
|
const num = ref(0);
|
||||||
num: 0,
|
const payLoading = ref(false);
|
||||||
payLoading: false,
|
const payError = ref('');
|
||||||
payError: '',
|
let interval = null;
|
||||||
};
|
|
||||||
},
|
const payWayName = computed(() => {
|
||||||
computed: {
|
return params.value.paymentMethod === 'ALIPAY' ? '支付宝' : '微信';
|
||||||
payWayName () {
|
});
|
||||||
return this.params.paymentMethod === 'ALIPAY' ? '支付宝' : '微信';
|
|
||||||
},
|
function formatPayError (rawMessage) {
|
||||||
},
|
const message = (rawMessage || '').trim();
|
||||||
methods: {
|
if (!message || message === '支付业务异常,请稍后重试') {
|
||||||
formatPayError (rawMessage) {
|
return `${payWayName.value}支付暂不可用,请稍后重试或选择其他支付方式`;
|
||||||
const message = (rawMessage || '').trim();
|
}
|
||||||
if (!message || message === '支付业务异常,请稍后重试') {
|
return message;
|
||||||
return `${this.payWayName}支付暂不可用,请稍后重试或选择其他支付方式`;
|
}
|
||||||
|
|
||||||
|
function clearPayPoll () {
|
||||||
|
if (interval) {
|
||||||
|
clearInterval(interval);
|
||||||
|
interval = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePay () {
|
||||||
|
clearPayPoll();
|
||||||
|
const params = route.query;
|
||||||
|
payLoading.value = true;
|
||||||
|
payError.value = '';
|
||||||
|
qrcode.value = '';
|
||||||
|
pay(params, { skipMessage: true })
|
||||||
|
.then((res) => {
|
||||||
|
payLoading.value = false;
|
||||||
|
if (res.success && res.result) {
|
||||||
|
qrcode.value = res.result;
|
||||||
|
num.value = 0;
|
||||||
|
interval = setInterval(callback, 5000);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return message;
|
payError.value = formatPayError(res.message);
|
||||||
},
|
Message.error(payError.value);
|
||||||
clearPayPoll () {
|
})
|
||||||
if (this.interval) {
|
.catch((err) => {
|
||||||
clearInterval(this.interval);
|
payLoading.value = false;
|
||||||
this.interval = null;
|
payError.value = formatPayError(err?.message || err?.data?.message);
|
||||||
}
|
Message.error(payError.value);
|
||||||
},
|
});
|
||||||
handlePay () {
|
}
|
||||||
this.clearPayPoll();
|
|
||||||
const params = this.$route.query;
|
function callback () {
|
||||||
this.payLoading = true;
|
num.value++;
|
||||||
this.payError = '';
|
if (num.value >= 7) {
|
||||||
this.qrcode = '';
|
clearPayPoll();
|
||||||
pay(params, { skipMessage: true })
|
}
|
||||||
.then((res) => {
|
const params = JSON.parse(JSON.stringify(route.query));
|
||||||
this.payLoading = false;
|
delete params.paymentMethod;
|
||||||
if (res.success && res.result) {
|
delete params.paymentClient;
|
||||||
this.qrcode = res.result;
|
payCallback(params).then((res) => {
|
||||||
this.num = 0;
|
if (res.result) {
|
||||||
this.interval = setInterval(this.callback, 5000);
|
clearPayPoll();
|
||||||
return;
|
router.push({ path: '/payDone', query: { orderType: route.query.orderType } });
|
||||||
}
|
}
|
||||||
this.payError = this.formatPayError(res.message);
|
});
|
||||||
Message.error(this.payError);
|
}
|
||||||
})
|
|
||||||
.catch((err) => {
|
onMounted(() => {
|
||||||
this.payLoading = false;
|
handlePay();
|
||||||
this.payError = this.formatPayError(err?.message || err?.data?.message);
|
});
|
||||||
Message.error(this.payError);
|
|
||||||
});
|
onBeforeUnmount(() => {
|
||||||
},
|
clearPayPoll();
|
||||||
callback () {
|
});
|
||||||
this.num++;
|
|
||||||
if (this.num >= 7) {
|
|
||||||
this.clearPayPoll();
|
|
||||||
}
|
|
||||||
const params = JSON.parse(JSON.stringify(this.$route.query));
|
|
||||||
delete params.paymentMethod;
|
|
||||||
delete params.paymentClient;
|
|
||||||
payCallback(params).then((res) => {
|
|
||||||
if (res.result) {
|
|
||||||
this.clearPayPoll();
|
|
||||||
this.$router.push({ path: '/payDone', query: { orderType: this.$route.query.orderType } });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
this.handlePay();
|
|
||||||
},
|
|
||||||
beforeUnmount () {
|
|
||||||
this.clearPayPoll();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.head-left {
|
.head-left {
|
||||||
|
|||||||
@@ -93,6 +93,18 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "PointGoodsDetail",
|
||||||
|
beforeRouteEnter(to, from, next) {
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, onMounted, nextTick } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
import Search from "@/components/Search";
|
import Search from "@/components/Search";
|
||||||
import { addCartGoods } from "@/api/cart.js";
|
import { addCartGoods } from "@/api/cart.js";
|
||||||
@@ -104,115 +116,109 @@ import {
|
|||||||
E_COUPON_CART_POINTS,
|
E_COUPON_CART_POINTS,
|
||||||
} from "@/constants/goodsType";
|
} from "@/constants/goodsType";
|
||||||
|
|
||||||
export default {
|
const route = useRoute();
|
||||||
name: "PointGoodsDetail",
|
const router = useRouter();
|
||||||
components: { Search },
|
|
||||||
beforeRouteEnter(to, from, next) {
|
const goodsMsg = ref({});
|
||||||
window.scrollTo(0, 0);
|
const goodsSku = ref({});
|
||||||
next();
|
const isLoading = ref(false);
|
||||||
},
|
const onceFlag = ref(true);
|
||||||
created() {
|
const count = ref(1);
|
||||||
this.getGoodsDetail();
|
const loading = ref(false);
|
||||||
},
|
const itemIntroGoods = ref(null);
|
||||||
mounted() {
|
|
||||||
window.addEventListener("scroll", this.handleScroll);
|
const isECouponSku = computed(() => {
|
||||||
},
|
return isECoupon(goodsSku.value.goodsType);
|
||||||
data() {
|
});
|
||||||
return {
|
const displayStock = computed(() => {
|
||||||
goodsMsg: {},
|
if (isECouponSku.value) {
|
||||||
goodsSku: {},
|
return getECouponStock(goodsSku.value);
|
||||||
isLoading: false,
|
}
|
||||||
onceFlag: true,
|
return Number(goodsSku.value.quantity) || 0;
|
||||||
count: 1,
|
});
|
||||||
loading: false,
|
const quantityMax = computed(() => {
|
||||||
};
|
if (isECouponSku.value) {
|
||||||
},
|
return getECouponMaxBuyNum(goodsSku.value);
|
||||||
computed: {
|
}
|
||||||
isECouponSku() {
|
const qty = Number(goodsSku.value.quantity) || 0;
|
||||||
return isECoupon(this.goodsSku.goodsType);
|
return qty > 0 ? qty : 1;
|
||||||
},
|
});
|
||||||
displayStock() {
|
const isOutOfStock = computed(() => {
|
||||||
if (this.isECouponSku) {
|
return displayStock.value <= 0;
|
||||||
return getECouponStock(this.goodsSku);
|
});
|
||||||
|
|
||||||
|
function getGoodsDetail() {
|
||||||
|
isLoading.value = true;
|
||||||
|
pointGoodsDetail(route.query.id)
|
||||||
|
.then((res) => {
|
||||||
|
isLoading.value = false;
|
||||||
|
if (res.success) {
|
||||||
|
goodsMsg.value = res.result;
|
||||||
|
goodsSku.value = res.result.goodsSku || {};
|
||||||
|
syncCountWithQuantity();
|
||||||
|
} else {
|
||||||
|
Message.error(res.message);
|
||||||
|
router.push("/");
|
||||||
}
|
}
|
||||||
return Number(this.goodsSku.quantity) || 0;
|
})
|
||||||
},
|
.catch(() => {
|
||||||
quantityMax() {
|
router.push("/");
|
||||||
if (this.isECouponSku) {
|
});
|
||||||
return getECouponMaxBuyNum(this.goodsSku);
|
}
|
||||||
|
|
||||||
|
function syncCountWithQuantity() {
|
||||||
|
const max = quantityMax.value;
|
||||||
|
if (max <= 0) {
|
||||||
|
count.value = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (count.value > max) count.value = max;
|
||||||
|
if (count.value < 1) count.value = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pointBuy() {
|
||||||
|
const params = {
|
||||||
|
num: count.value,
|
||||||
|
skuId: goodsMsg.value.skuId,
|
||||||
|
cartType: E_COUPON_CART_POINTS,
|
||||||
|
};
|
||||||
|
loading.value = true;
|
||||||
|
addCartGoods(params)
|
||||||
|
.then((res) => {
|
||||||
|
loading.value = false;
|
||||||
|
if (res.success) {
|
||||||
|
router.push({ path: "/pay", query: { way: params.cartType } });
|
||||||
|
} else {
|
||||||
|
Message.warning(res.message);
|
||||||
}
|
}
|
||||||
const qty = Number(this.goodsSku.quantity) || 0;
|
})
|
||||||
return qty > 0 ? qty : 1;
|
.catch(() => {
|
||||||
},
|
loading.value = false;
|
||||||
isOutOfStock() {
|
});
|
||||||
return this.displayStock <= 0;
|
}
|
||||||
},
|
|
||||||
},
|
function handleScroll() {
|
||||||
methods: {
|
if (onceFlag.value) {
|
||||||
getGoodsDetail() {
|
nextTick(() => {
|
||||||
this.isLoading = true;
|
changeHeight();
|
||||||
pointGoodsDetail(this.$route.query.id)
|
});
|
||||||
.then((res) => {
|
onceFlag.value = false;
|
||||||
this.isLoading = false;
|
}
|
||||||
if (res.success) {
|
}
|
||||||
this.goodsMsg = res.result;
|
|
||||||
this.goodsSku = res.result.goodsSku || {};
|
function changeHeight() {
|
||||||
this.syncCountWithQuantity();
|
const goodsDetailCon = document.querySelector(".item-intro");
|
||||||
} else {
|
if (!goodsDetailCon || !itemIntroGoods.value) return;
|
||||||
Message.error(res.message);
|
let heightCss = window.getComputedStyle(goodsDetailCon).height;
|
||||||
this.$router.push("/");
|
heightCss = parseInt(heightCss.substr(0, heightCss.length - 2), 10) + 89;
|
||||||
}
|
itemIntroGoods.value.style.height = heightCss + "px";
|
||||||
})
|
}
|
||||||
.catch(() => {
|
|
||||||
this.$router.push("/");
|
getGoodsDetail();
|
||||||
});
|
|
||||||
},
|
onMounted(() => {
|
||||||
syncCountWithQuantity() {
|
window.addEventListener("scroll", handleScroll);
|
||||||
const max = this.quantityMax;
|
});
|
||||||
if (max <= 0) {
|
|
||||||
this.count = 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.count > max) this.count = max;
|
|
||||||
if (this.count < 1) this.count = 1;
|
|
||||||
},
|
|
||||||
pointBuy() {
|
|
||||||
const params = {
|
|
||||||
num: this.count,
|
|
||||||
skuId: this.goodsMsg.skuId,
|
|
||||||
cartType: E_COUPON_CART_POINTS,
|
|
||||||
};
|
|
||||||
this.loading = true;
|
|
||||||
addCartGoods(params)
|
|
||||||
.then((res) => {
|
|
||||||
this.loading = false;
|
|
||||||
if (res.success) {
|
|
||||||
this.$router.push({ path: "/pay", query: { way: params.cartType } });
|
|
||||||
} else {
|
|
||||||
Message.warning(res.message);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
this.loading = false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
handleScroll() {
|
|
||||||
if (this.onceFlag) {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.changeHeight();
|
|
||||||
});
|
|
||||||
this.onceFlag = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
changeHeight() {
|
|
||||||
const goodsDetailCon = document.querySelector(".item-intro");
|
|
||||||
if (!goodsDetailCon || !this.$refs.itemIntroGoods) return;
|
|
||||||
let heightCss = window.getComputedStyle(goodsDetailCon).height;
|
|
||||||
heightCss = parseInt(heightCss.substr(0, heightCss.length - 2), 10) + 89;
|
|
||||||
this.$refs.itemIntroGoods.style.height = heightCss + "px";
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -44,78 +44,92 @@
|
|||||||
<BaseFooter></BaseFooter>
|
<BaseFooter></BaseFooter>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
import {pointGoods, pointGoodsCategory} from '@/api/promotion.js'
|
import { onMounted, reactive, ref } from "vue";
|
||||||
export default {
|
import { useRoute, useRouter } from "vue-router";
|
||||||
data () {
|
import { pointGoods, pointGoodsCategory } from "@/api/promotion.js";
|
||||||
return {
|
|
||||||
goodsList: [], // 积分商品列表
|
const route = useRoute();
|
||||||
cateList: [{ // 商品分类
|
const router = useRouter();
|
||||||
name: '全部分类',
|
|
||||||
id: 0
|
const goodsList = ref([]); // 积分商品列表
|
||||||
}], // 积分分类列表
|
const cateList = ref([{ // 商品分类
|
||||||
params: { // 商品列表请求参数
|
name: "全部分类",
|
||||||
pageNumber: 1,
|
id: 0,
|
||||||
pageSize: 20,
|
}]); // 积分分类列表
|
||||||
pointsGoodsCategoryId: ''
|
// 商品列表请求参数
|
||||||
},
|
const params = reactive({
|
||||||
total: 0, // 商品总数
|
pageNumber: 1,
|
||||||
cateId: '' // 店铺分类id
|
pageSize: 20,
|
||||||
|
pointsGoodsCategoryId: "",
|
||||||
|
});
|
||||||
|
const total = ref(0); // 商品总数
|
||||||
|
const cateId = ref(""); // 店铺分类id
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
params.pointsGoodsCategoryId = route.query.categoryId || "";
|
||||||
|
cateId.value = route.query.categoryId || 0;
|
||||||
|
getList();
|
||||||
|
getCate();
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 列表 API 返回 PointsGoods(thumbnail/goodsName 在顶层);详情 API 才有 goodsSku */
|
||||||
|
function itemThumbnail(item) {
|
||||||
|
return item?.goodsSku?.thumbnail || item?.thumbnail || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function itemGoodsName(item) {
|
||||||
|
return item?.goodsSku?.goodsName || item?.goodsName || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取列表
|
||||||
|
function getList() {
|
||||||
|
pointGoods(params).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
goodsList.value = res.result.records;
|
||||||
|
total.value = res.result.total;
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
mounted () {
|
}
|
||||||
this.params.pointsGoodsCategoryId = this.$route.query.categoryId || ''
|
|
||||||
this.cateId = this.$route.query.categoryId || 0
|
// 获取分类
|
||||||
this.getList()
|
function getCate() {
|
||||||
this.getCate()
|
pointGoodsCategory(params).then((res) => {
|
||||||
},
|
if (res.success) {
|
||||||
methods: {
|
cateList.value.push(...res.result.records);
|
||||||
/** 列表 API 返回 PointsGoods(thumbnail/goodsName 在顶层);详情 API 才有 goodsSku */
|
|
||||||
itemThumbnail (item) {
|
|
||||||
return item?.goodsSku?.thumbnail || item?.thumbnail || ''
|
|
||||||
},
|
|
||||||
itemGoodsName (item) {
|
|
||||||
return item?.goodsSku?.goodsName || item?.goodsName || ''
|
|
||||||
},
|
|
||||||
getList () { // 获取列表
|
|
||||||
pointGoods(this.params).then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
this.goodsList = res.result.records
|
|
||||||
this.total = res.result.total
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
getCate () { // 获取分类
|
|
||||||
pointGoodsCategory(this.params).then(res => {
|
|
||||||
if (res.success) {
|
|
||||||
this.cateList.push(...res.result.records)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
selectCate (item) { // 选择商品分类
|
|
||||||
let cateId = item.value === 0 ? '' : item.value
|
|
||||||
this.params.pointsGoodsCategoryId = cateId
|
|
||||||
this.getList()
|
|
||||||
this.$router.push({query: {categoryId: cateId}})
|
|
||||||
},
|
|
||||||
goGoodsDetail (id) { // 跳转商品详情
|
|
||||||
this.$router.push({path: '/pointGoodsDetail', query: {id}})
|
|
||||||
// let routerUrl = this.$router.resolve({
|
|
||||||
// path: '/pointGoodsDetail',
|
|
||||||
// query: {id}
|
|
||||||
// })
|
|
||||||
// window.open(routerUrl.href, '_blank')
|
|
||||||
},
|
|
||||||
changePageNum (val) { // 修改页码
|
|
||||||
this.params.pageNumber = val;
|
|
||||||
this.getList()
|
|
||||||
},
|
|
||||||
changePageSize (val) { // 修改页数
|
|
||||||
this.params.pageNumber = 1;
|
|
||||||
this.params.pageSize = val;
|
|
||||||
this.getList()
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择商品分类
|
||||||
|
function selectCate(item) {
|
||||||
|
let cateId = item.value === 0 ? "" : item.value;
|
||||||
|
params.pointsGoodsCategoryId = cateId;
|
||||||
|
getList();
|
||||||
|
router.push({ query: { categoryId: cateId } });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳转商品详情
|
||||||
|
function goGoodsDetail(id) {
|
||||||
|
router.push({ path: "/pointGoodsDetail", query: { id } });
|
||||||
|
// let routerUrl = router.resolve({
|
||||||
|
// path: '/pointGoodsDetail',
|
||||||
|
// query: {id}
|
||||||
|
// })
|
||||||
|
// window.open(routerUrl.href, '_blank')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改页码
|
||||||
|
function changePageNum(val) {
|
||||||
|
params.pageNumber = val;
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改页数
|
||||||
|
function changePageSize(val) {
|
||||||
|
params.pageNumber = 1;
|
||||||
|
params.pageSize = val;
|
||||||
|
getList();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -61,121 +61,127 @@
|
|||||||
<BaseFooter />
|
<BaseFooter />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
|
import { onBeforeUnmount, onMounted, ref, watch } from "vue";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
import { seckillByDay } from "@/api/promotion";
|
import { seckillByDay } from "@/api/promotion";
|
||||||
export default {
|
|
||||||
data () {
|
const router = useRouter();
|
||||||
return {
|
|
||||||
list: [], // 秒杀时段列表
|
const list = ref([]); // 秒杀时段列表
|
||||||
goodsList: [], // 商品列表
|
const goodsList = ref([]); // 商品列表
|
||||||
interval: null, // 定时器
|
let interval = null; // 定时器
|
||||||
currIndex: 0, // 当前时间段的下标
|
const currIndex = ref(0); // 当前时间段的下标
|
||||||
currTime: 0, // 当前显示的倒计时
|
const currTime = ref(0); // 当前显示的倒计时
|
||||||
diffSeconds: 0, // 倒计时时间戳
|
const diffSeconds = ref(0); // 倒计时时间戳
|
||||||
nowHour: new Date().getHours(), // 当前小时数
|
const nowHour = ref(new Date().getHours()); // 当前小时数
|
||||||
};
|
|
||||||
},
|
watch(currIndex, (val) => {
|
||||||
beforeUnmount () {
|
clearInterval(interval);
|
||||||
clearInterval(this.interval);
|
interval = null;
|
||||||
},
|
nowHour.value = new Date().getHours();
|
||||||
watch: {
|
countDown(val);
|
||||||
currIndex (val) {
|
goodsList.value = list.value[val].seckillGoodsList;
|
||||||
clearInterval(this.interval);
|
});
|
||||||
this.interval = null;
|
|
||||||
this.nowHour = new Date().getHours();
|
watch(diffSeconds, (val) => {
|
||||||
this.countDown(val);
|
const hours = Math.floor(val / 3600);
|
||||||
this.goodsList = this.list[val].seckillGoodsList;
|
// 当前秒数 / 60,向下取整
|
||||||
},
|
// 获取到所有分钟数 3600 / 60 = 60分钟
|
||||||
diffSeconds (val) {
|
// 对60取模,超过小时数的分钟数
|
||||||
const hours = Math.floor(val / 3600);
|
const minutes = Math.floor(val / 60) % 60;
|
||||||
// 当前秒数 / 60,向下取整
|
// 当前的秒数 % 60,获取到 超过小时数、分钟数的秒数(秒数)
|
||||||
// 获取到所有分钟数 3600 / 60 = 60分钟
|
const seconds = val % 60;
|
||||||
// 对60取模,超过小时数的分钟数
|
currTime.value =
|
||||||
const minutes = Math.floor(val / 60) % 60;
|
filteTime(hours) + ":" + filteTime(minutes) + ":" + filteTime(seconds);
|
||||||
// 当前的秒数 % 60,获取到 超过小时数、分钟数的秒数(秒数)
|
if (val <= 0) {
|
||||||
const seconds = val % 60;
|
clearInterval(interval);
|
||||||
this.currTime =
|
interval = null;
|
||||||
filteTime(hours) + ":" + filteTime(minutes) + ":" + filteTime(seconds);
|
}
|
||||||
if (val <= 0) {
|
function filteTime (time) {
|
||||||
clearInterval(this.interval);
|
if (time < 10) {
|
||||||
this.interval = null;
|
return "0" + time;
|
||||||
}
|
} else {
|
||||||
function filteTime (time) {
|
return time;
|
||||||
if (time < 10) {
|
}
|
||||||
return "0" + time;
|
}
|
||||||
} else {
|
});
|
||||||
return time;
|
|
||||||
}
|
function getListByDay () {
|
||||||
}
|
// 当天秒杀活动
|
||||||
},
|
seckillByDay().then((res) => {
|
||||||
},
|
if (res.success) {
|
||||||
methods: {
|
list.value = res.result;
|
||||||
getListByDay () {
|
goodsList.value = list.value[0].seckillGoodsList;
|
||||||
// 当天秒杀活动
|
countDown(currIndex.value);
|
||||||
seckillByDay().then((res) => {
|
}
|
||||||
if (res.success) {
|
});
|
||||||
this.list = res.result;
|
}
|
||||||
this.goodsList = this.list[0].seckillGoodsList;
|
|
||||||
this.countDown(this.currIndex);
|
function goGoodsDetail (skuId, goodsId) {
|
||||||
}
|
// 跳转商品详情
|
||||||
});
|
let routeUrl = router.resolve({
|
||||||
},
|
path: "/goodsDetail",
|
||||||
goGoodsDetail (skuId, goodsId) {
|
query: { skuId, goodsId },
|
||||||
// 跳转商品详情
|
});
|
||||||
let routeUrl = this.$router.resolve({
|
window.open(routeUrl.href, "_blank");
|
||||||
path: "/goodsDetail",
|
}
|
||||||
query: { skuId, goodsId },
|
|
||||||
});
|
// 已售占比。没有库存时按售罄展示
|
||||||
window.open(routeUrl.href, "_blank");
|
function salePercent (item) {
|
||||||
},
|
if (!item.quantity || item.quantity <= 0) return 100;
|
||||||
// 已售占比。没有库存时按售罄展示
|
return Math.min(100, Math.ceil((item.salesNum / item.quantity) * 100));
|
||||||
salePercent (item) {
|
}
|
||||||
if (!item.quantity || item.quantity <= 0) return 100;
|
|
||||||
return Math.min(100, Math.ceil((item.salesNum / item.quantity) * 100));
|
// 场次已开始,且销量达到库存
|
||||||
},
|
function isSoldOut (item) {
|
||||||
// 场次已开始,且销量达到库存
|
return nowHour.value >= list.value[currIndex.value].timeLine && item.quantity <= item.salesNum;
|
||||||
isSoldOut (item) {
|
}
|
||||||
return this.nowHour >= this.list[this.currIndex].timeLine && item.quantity <= item.salesNum;
|
|
||||||
},
|
// 场次已开始且未售罄才可抢
|
||||||
// 场次已开始且未售罄才可抢
|
function canBuy (item) {
|
||||||
canBuy (item) {
|
return nowHour.value >= list.value[currIndex.value].timeLine && !isSoldOut(item);
|
||||||
return this.nowHour >= this.list[this.currIndex].timeLine && !this.isSoldOut(item);
|
}
|
||||||
},
|
|
||||||
// 按钮文案:未开始 / 已售罄 / 立即抢购
|
// 按钮文案:未开始 / 已售罄 / 立即抢购
|
||||||
buyText (item) {
|
function buyText (item) {
|
||||||
if (this.nowHour < this.list[this.currIndex].timeLine) return "即将开始";
|
if (nowHour.value < list.value[currIndex.value].timeLine) return "即将开始";
|
||||||
if (this.isSoldOut(item)) return "已售罄";
|
if (isSoldOut(item)) return "已售罄";
|
||||||
return "立即抢购";
|
return "立即抢购";
|
||||||
},
|
}
|
||||||
countDown (currIndex) {
|
|
||||||
// 倒计时
|
function countDown (currIndex) {
|
||||||
// 0点时间戳
|
// 倒计时
|
||||||
let zeroTime = new Date(new Date().toLocaleDateString()).getTime();
|
// 0点时间戳
|
||||||
let currTime = new Date().getTime();
|
let zeroTime = new Date(new Date().toLocaleDateString()).getTime();
|
||||||
let actTime = 0;
|
let currTime = new Date().getTime();
|
||||||
let nowHour = new Date().getHours(); // 当前小时数
|
let actTime = 0;
|
||||||
if (this.list[currIndex].timeLine > nowHour) {
|
let nowHour = new Date().getHours(); // 当前小时数
|
||||||
// 活动未开始
|
if (list.value[currIndex].timeLine > nowHour) {
|
||||||
actTime = zeroTime + this.list[currIndex].timeLine * 3600 * 1000;
|
// 活动未开始
|
||||||
} else if (this.list[currIndex].timeLine <= nowHour) {
|
actTime = zeroTime + list.value[currIndex].timeLine * 3600 * 1000;
|
||||||
// 活动进行中
|
} else if (list.value[currIndex].timeLine <= nowHour) {
|
||||||
if (currIndex === this.list.length - 1) {
|
// 活动进行中
|
||||||
// 如果是最后一个活动,直到24点结束
|
if (currIndex === list.value.length - 1) {
|
||||||
actTime = zeroTime + 24 * 3600 * 1000;
|
// 如果是最后一个活动,直到24点结束
|
||||||
} else {
|
actTime = zeroTime + 24 * 3600 * 1000;
|
||||||
actTime = zeroTime + this.list[currIndex + 1].timeLine * 3600 * 1000;
|
} else {
|
||||||
}
|
actTime = zeroTime + list.value[currIndex + 1].timeLine * 3600 * 1000;
|
||||||
}
|
}
|
||||||
this.diffSeconds = Math.floor((actTime - currTime) / 1000);
|
}
|
||||||
this.interval = setInterval(() => {
|
diffSeconds.value = Math.floor((actTime - currTime) / 1000);
|
||||||
this.diffSeconds--;
|
interval = setInterval(() => {
|
||||||
}, 1000);
|
diffSeconds.value--;
|
||||||
},
|
}, 1000);
|
||||||
},
|
}
|
||||||
mounted () {
|
|
||||||
this.getListByDay();
|
onMounted(() => {
|
||||||
},
|
getListByDay();
|
||||||
};
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
clearInterval(interval);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "../../assets/styles/goodsList.scss";
|
@import "../../assets/styles/goodsList.scss";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="company-msg">
|
<div class="company-msg">
|
||||||
<el-form
|
<el-form
|
||||||
ref="firstForm"
|
ref="firstFormRef"
|
||||||
:model="form"
|
:model="form"
|
||||||
:rules="rules"
|
:rules="rules"
|
||||||
label-width="140px"
|
label-width="140px"
|
||||||
@@ -190,7 +190,8 @@
|
|||||||
<multipleMap ref="map" @callback="getAddress" />
|
<multipleMap ref="map" @callback="getAddress" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
|
import { onBeforeUnmount, onMounted, reactive, ref, watch } from "vue";
|
||||||
import { Message, Notice } from "@/utils/message";
|
import { Message, Notice } from "@/utils/message";
|
||||||
import { Delete, View } from '@element-plus/icons-vue';
|
import { Delete, View } from '@element-plus/icons-vue';
|
||||||
import { applyFirst } from '@/api/shopentry';
|
import { applyFirst } from '@/api/shopentry';
|
||||||
@@ -200,261 +201,259 @@ import storage from '@/plugins/storage';
|
|||||||
import { buyerUrl } from '@/plugins/request.js';
|
import { buyerUrl } from '@/plugins/request.js';
|
||||||
const FIRST_APPLY_DRAFT_KEY = 'shopEntryFirstDraft';
|
const FIRST_APPLY_DRAFT_KEY = 'shopEntryFirstDraft';
|
||||||
|
|
||||||
export default {
|
const emit = defineEmits(['change']);
|
||||||
components: { multipleMap, View, Delete },
|
const props = defineProps({
|
||||||
emits: ['change'],
|
content: {
|
||||||
props: {
|
default: () => ({}),
|
||||||
content: {
|
type: Object
|
||||||
default: () => ({}),
|
|
||||||
type: Object
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
action: buyerUrl + '/buyer/common/upload/file', // 上传地址
|
|
||||||
accessToken: {}, // 验证token
|
|
||||||
visible: false, // 预览图片
|
|
||||||
loading: false, // 加载状态
|
|
||||||
|
|
||||||
previewPicture: '', // 预览图片url
|
|
||||||
form: { // 表单数据
|
|
||||||
companyName: '',
|
|
||||||
companyAddressIdPath: '',
|
|
||||||
companyAddressPath: '',
|
|
||||||
companyAddress: '',
|
|
||||||
employeeNum: '',
|
|
||||||
companyPhone: '',
|
|
||||||
registeredCapital: '',
|
|
||||||
linkName: '',
|
|
||||||
linkPhone: '',
|
|
||||||
companyEmail: '',
|
|
||||||
licenseNum: '',
|
|
||||||
scope: '',
|
|
||||||
legalName: '',
|
|
||||||
legalId: '',
|
|
||||||
legalPhoto: [],
|
|
||||||
licencePhoto: []
|
|
||||||
},
|
|
||||||
rules: { // 验证规则
|
|
||||||
companyName: [{ required: true, message: '请填写公司信息' }],
|
|
||||||
companyAddressIdPath: [{ required: true, message: '请选择公司所在地' }],
|
|
||||||
companyAddress: [{ required: true, message: '请填写公司详细地址' }],
|
|
||||||
employeeNum: [
|
|
||||||
{ required: true, message: '请填写公司员工总数' },
|
|
||||||
{ pattern: RegExp.integer, message: '只能填写正整数' }
|
|
||||||
],
|
|
||||||
registeredCapital: [
|
|
||||||
{ required: true, message: '请填写公司注册资金' },
|
|
||||||
{ pattern: RegExp.integer, message: '只能填写正整数' }
|
|
||||||
],
|
|
||||||
linkName: [{ required: true, message: '请填写联系人姓名' }],
|
|
||||||
linkPhone: [
|
|
||||||
{ required: true, message: '请填写联系人电话' },
|
|
||||||
{ pattern: RegExp.mobile, message: '请填写正确的号码' }
|
|
||||||
],
|
|
||||||
companyPhone: [
|
|
||||||
{ required: true, message: '请填写公司电话' },
|
|
||||||
{ pattern: RegExp.mobile, message: '请填写正确的号码' }
|
|
||||||
],
|
|
||||||
companyEmail: [
|
|
||||||
{ required: true, message: '请填写电子邮箱' },
|
|
||||||
{ type: 'email', message: '请输入正确的邮箱' }
|
|
||||||
],
|
|
||||||
licenseNum: [
|
|
||||||
{ required: true, message: '请填写营业执照号' },
|
|
||||||
{ pattern: RegExp.licenseNum, message: '请输入正确的营业执照号' }
|
|
||||||
],
|
|
||||||
scope: [{ required: true, message: '请填写营业执照所示经营范围' }],
|
|
||||||
legalPhoto: [{ required: true, message: '请上传法人身份证照片' }],
|
|
||||||
licencePhoto: [{ required: true, message: '请上传营业执照' }],
|
|
||||||
legalName: [{ required: true, message: '请输入法人姓名' }],
|
|
||||||
legalId: [
|
|
||||||
{ required: true, message: '请输入法人证件号' },
|
|
||||||
{ pattern: RegExp.IDCard, message: '请输入正确的证件号' }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
uploadLoading1: false, // 上传loading
|
|
||||||
uploadLoading: false, // 上传loading
|
|
||||||
contentInitialized: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// 获取店铺地址
|
|
||||||
getAddress(val){
|
|
||||||
if(val.type === 'select'){
|
|
||||||
const paths = val.data.map(item => item.name).join(',')
|
|
||||||
const ids = val.data.map(item => item.id).join(',')
|
|
||||||
this.form['companyAddressIdPath'] = ids
|
|
||||||
this.form['companyAddressPath'] = paths
|
|
||||||
}else{
|
|
||||||
this.form['companyAddressIdPath'] = val.data.addrId
|
|
||||||
this.form['companyAddressPath'] = val.data.addr
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 下一步
|
|
||||||
next () {
|
|
||||||
this.$refs.firstForm.validate((valid) => {
|
|
||||||
if (valid) {
|
|
||||||
this.loading = true;
|
|
||||||
let params = JSON.parse(JSON.stringify(this.form));
|
|
||||||
params.legalPhoto = this.form.legalPhoto.toString();
|
|
||||||
params.licencePhoto = this.normalizeLicencePhoto(this.form.licencePhoto);
|
|
||||||
applyFirst(params)
|
|
||||||
.then((res) => {
|
|
||||||
this.loading = false;
|
|
||||||
if (res.success) {
|
|
||||||
sessionStorage.removeItem(FIRST_APPLY_DRAFT_KEY);
|
|
||||||
this.$emit('change', 1);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
this.loading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// 上传之前
|
|
||||||
beforeUpload (file) {
|
|
||||||
this.uploadLoading = true;
|
|
||||||
if (this.form.licencePhoto.length >= 1) {
|
|
||||||
Message.warning('最多上传一张图片')
|
|
||||||
this.uploadLoading = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return this.validateUploadFile(file);
|
|
||||||
},
|
|
||||||
// 上传之前
|
|
||||||
beforeUpload1 (file) {
|
|
||||||
this.uploadLoading1 = true;
|
|
||||||
if (this.form.legalPhoto.length >= 2) {
|
|
||||||
Message.warning('最多上传两张图片')
|
|
||||||
this.uploadLoading1 = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return this.validateUploadFile(file);
|
|
||||||
},
|
|
||||||
validateUploadFile (file) {
|
|
||||||
const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'];
|
|
||||||
const isAllowedType = allowedTypes.includes(file.type);
|
|
||||||
const isLt2M = file.size / 1024 / 1024 < 2;
|
|
||||||
if (!isAllowedType) {
|
|
||||||
this.uploadLoading = false;
|
|
||||||
this.uploadLoading1 = false;
|
|
||||||
Message.warning('上传文件格式不正确');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!isLt2M) {
|
|
||||||
this.uploadLoading = false;
|
|
||||||
this.uploadLoading1 = false;
|
|
||||||
Message.warning('文件大小不能超过2M');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
// 上传成功回调
|
|
||||||
handleSuccess (res, file) {
|
|
||||||
this.uploadLoading = false;
|
|
||||||
this.form.licencePhoto = [res.result];
|
|
||||||
},
|
|
||||||
// 上传成功回调
|
|
||||||
handleSuccess1 (res, file) {
|
|
||||||
this.uploadLoading1 = false;
|
|
||||||
this.form.legalPhoto.push(res.result);
|
|
||||||
},
|
|
||||||
// 上传失败
|
|
||||||
uploadErr () {
|
|
||||||
this.uploadLoading = false;
|
|
||||||
this.uploadLoading1 = false;
|
|
||||||
},
|
|
||||||
// 上传失败回调
|
|
||||||
handleFormatError (file) {
|
|
||||||
this.uploadLoading = false;
|
|
||||||
this.uploadLoading1 = false;
|
|
||||||
Notice.warning({
|
|
||||||
title: 'The file format is incorrect',
|
|
||||||
desc: '上传文件格式不正确'
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 上传大小限制
|
|
||||||
handleMaxSize (file) {
|
|
||||||
this.uploadLoading = false;
|
|
||||||
this.uploadLoading1 = false;
|
|
||||||
Notice.warning({
|
|
||||||
title: 'Exceeding file size limit',
|
|
||||||
desc: '文件大小不能超过2M'
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 图片查看
|
|
||||||
handleView (item) {
|
|
||||||
this.previewPicture = item;
|
|
||||||
this.visible = true;
|
|
||||||
},
|
|
||||||
// 删除图片
|
|
||||||
handleRemove (index, listName) {
|
|
||||||
this.form[listName].splice(index, 1);
|
|
||||||
},
|
|
||||||
normalizeLicencePhoto (value) {
|
|
||||||
if (Array.isArray(value)) {
|
|
||||||
return value.length ? value[0] : '';
|
|
||||||
}
|
|
||||||
return value ? String(value).split(',').filter(Boolean)[0] || '' : '';
|
|
||||||
},
|
|
||||||
initFormFromContent(content) {
|
|
||||||
if (!content || !Object.keys(content).length) return;
|
|
||||||
this.form = JSON.parse(JSON.stringify(content));
|
|
||||||
this.form.legalPhoto = content.legalPhoto
|
|
||||||
? String(content.legalPhoto).split(',').filter(Boolean)
|
|
||||||
: [];
|
|
||||||
this.form.licencePhoto = content.licencePhoto
|
|
||||||
? String(content.licencePhoto).split(',').filter(Boolean).slice(0, 1)
|
|
||||||
: [];
|
|
||||||
this.contentInitialized = true;
|
|
||||||
},
|
|
||||||
restoreDraft() {
|
|
||||||
const draft = sessionStorage.getItem(FIRST_APPLY_DRAFT_KEY);
|
|
||||||
if (!draft) return;
|
|
||||||
try {
|
|
||||||
const parsed = JSON.parse(draft);
|
|
||||||
this.form = {
|
|
||||||
legalPhoto: [],
|
|
||||||
licencePhoto: [],
|
|
||||||
...parsed,
|
|
||||||
};
|
|
||||||
this.form.legalPhoto = Array.isArray(parsed.legalPhoto) ? parsed.legalPhoto : [];
|
|
||||||
this.form.licencePhoto = Array.isArray(parsed.licencePhoto)
|
|
||||||
? parsed.licencePhoto.slice(0, 1)
|
|
||||||
: [];
|
|
||||||
} catch (e) {
|
|
||||||
sessionStorage.removeItem(FIRST_APPLY_DRAFT_KEY);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
saveDraft() {
|
|
||||||
sessionStorage.setItem(FIRST_APPLY_DRAFT_KEY, JSON.stringify(this.form));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
content: {
|
|
||||||
immediate: true,
|
|
||||||
handler(val) {
|
|
||||||
if (this.contentInitialized || !val || !Object.keys(val).length) return;
|
|
||||||
this.initFormFromContent(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
this.accessToken.accessToken = storage.getItem('accessToken');
|
|
||||||
if (Object.keys(this.content).length) {
|
|
||||||
this.initFormFromContent(this.content);
|
|
||||||
} else {
|
|
||||||
this.restoreDraft();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
beforeUnmount() {
|
|
||||||
this.saveDraft();
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const firstFormRef = ref(null);
|
||||||
|
const map = ref(null);
|
||||||
|
|
||||||
|
const action = buyerUrl + '/buyer/common/upload/file'; // 上传地址
|
||||||
|
const accessToken = reactive({}); // 验证token
|
||||||
|
const visible = ref(false); // 预览图片
|
||||||
|
const loading = ref(false); // 加载状态
|
||||||
|
|
||||||
|
const previewPicture = ref(''); // 预览图片url
|
||||||
|
const form = ref({ // 表单数据
|
||||||
|
companyName: '',
|
||||||
|
companyAddressIdPath: '',
|
||||||
|
companyAddressPath: '',
|
||||||
|
companyAddress: '',
|
||||||
|
employeeNum: '',
|
||||||
|
companyPhone: '',
|
||||||
|
registeredCapital: '',
|
||||||
|
linkName: '',
|
||||||
|
linkPhone: '',
|
||||||
|
companyEmail: '',
|
||||||
|
licenseNum: '',
|
||||||
|
scope: '',
|
||||||
|
legalName: '',
|
||||||
|
legalId: '',
|
||||||
|
legalPhoto: [],
|
||||||
|
licencePhoto: []
|
||||||
|
});
|
||||||
|
const rules = { // 验证规则
|
||||||
|
companyName: [{ required: true, message: '请填写公司信息' }],
|
||||||
|
companyAddressIdPath: [{ required: true, message: '请选择公司所在地' }],
|
||||||
|
companyAddress: [{ required: true, message: '请填写公司详细地址' }],
|
||||||
|
employeeNum: [
|
||||||
|
{ required: true, message: '请填写公司员工总数' },
|
||||||
|
{ pattern: RegExp.integer, message: '只能填写正整数' }
|
||||||
|
],
|
||||||
|
registeredCapital: [
|
||||||
|
{ required: true, message: '请填写公司注册资金' },
|
||||||
|
{ pattern: RegExp.integer, message: '只能填写正整数' }
|
||||||
|
],
|
||||||
|
linkName: [{ required: true, message: '请填写联系人姓名' }],
|
||||||
|
linkPhone: [
|
||||||
|
{ required: true, message: '请填写联系人电话' },
|
||||||
|
{ pattern: RegExp.mobile, message: '请填写正确的号码' }
|
||||||
|
],
|
||||||
|
companyPhone: [
|
||||||
|
{ required: true, message: '请填写公司电话' },
|
||||||
|
{ pattern: RegExp.mobile, message: '请填写正确的号码' }
|
||||||
|
],
|
||||||
|
companyEmail: [
|
||||||
|
{ required: true, message: '请填写电子邮箱' },
|
||||||
|
{ type: 'email', message: '请输入正确的邮箱' }
|
||||||
|
],
|
||||||
|
licenseNum: [
|
||||||
|
{ required: true, message: '请填写营业执照号' },
|
||||||
|
{ pattern: RegExp.licenseNum, message: '请输入正确的营业执照号' }
|
||||||
|
],
|
||||||
|
scope: [{ required: true, message: '请填写营业执照所示经营范围' }],
|
||||||
|
legalPhoto: [{ required: true, message: '请上传法人身份证照片' }],
|
||||||
|
licencePhoto: [{ required: true, message: '请上传营业执照' }],
|
||||||
|
legalName: [{ required: true, message: '请输入法人姓名' }],
|
||||||
|
legalId: [
|
||||||
|
{ required: true, message: '请输入法人证件号' },
|
||||||
|
{ pattern: RegExp.IDCard, message: '请输入正确的证件号' }
|
||||||
|
]
|
||||||
};
|
};
|
||||||
|
const uploadLoading1 = ref(false); // 上传loading
|
||||||
|
const uploadLoading = ref(false); // 上传loading
|
||||||
|
const contentInitialized = ref(false);
|
||||||
|
|
||||||
|
// 获取店铺地址
|
||||||
|
function getAddress(val){
|
||||||
|
if(val.type === 'select'){
|
||||||
|
const paths = val.data.map(item => item.name).join(',')
|
||||||
|
const ids = val.data.map(item => item.id).join(',')
|
||||||
|
form.value['companyAddressIdPath'] = ids
|
||||||
|
form.value['companyAddressPath'] = paths
|
||||||
|
}else{
|
||||||
|
form.value['companyAddressIdPath'] = val.data.addrId
|
||||||
|
form.value['companyAddressPath'] = val.data.addr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下一步
|
||||||
|
function next () {
|
||||||
|
firstFormRef.value.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
loading.value = true;
|
||||||
|
let params = JSON.parse(JSON.stringify(form.value));
|
||||||
|
params.legalPhoto = form.value.legalPhoto.toString();
|
||||||
|
params.licencePhoto = normalizeLicencePhoto(form.value.licencePhoto);
|
||||||
|
applyFirst(params)
|
||||||
|
.then((res) => {
|
||||||
|
loading.value = false;
|
||||||
|
if (res.success) {
|
||||||
|
sessionStorage.removeItem(FIRST_APPLY_DRAFT_KEY);
|
||||||
|
emit('change', 1);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传之前
|
||||||
|
function beforeUpload (file) {
|
||||||
|
uploadLoading.value = true;
|
||||||
|
if (form.value.licencePhoto.length >= 1) {
|
||||||
|
Message.warning('最多上传一张图片')
|
||||||
|
uploadLoading.value = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return validateUploadFile(file);
|
||||||
|
}
|
||||||
|
// 上传之前
|
||||||
|
function beforeUpload1 (file) {
|
||||||
|
uploadLoading1.value = true;
|
||||||
|
if (form.value.legalPhoto.length >= 2) {
|
||||||
|
Message.warning('最多上传两张图片')
|
||||||
|
uploadLoading1.value = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return validateUploadFile(file);
|
||||||
|
}
|
||||||
|
function validateUploadFile (file) {
|
||||||
|
const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'];
|
||||||
|
const isAllowedType = allowedTypes.includes(file.type);
|
||||||
|
const isLt2M = file.size / 1024 / 1024 < 2;
|
||||||
|
if (!isAllowedType) {
|
||||||
|
uploadLoading.value = false;
|
||||||
|
uploadLoading1.value = false;
|
||||||
|
Message.warning('上传文件格式不正确');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!isLt2M) {
|
||||||
|
uploadLoading.value = false;
|
||||||
|
uploadLoading1.value = false;
|
||||||
|
Message.warning('文件大小不能超过2M');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 上传成功回调
|
||||||
|
function handleSuccess (res, file) {
|
||||||
|
uploadLoading.value = false;
|
||||||
|
form.value.licencePhoto = [res.result];
|
||||||
|
}
|
||||||
|
// 上传成功回调
|
||||||
|
function handleSuccess1 (res, file) {
|
||||||
|
uploadLoading1.value = false;
|
||||||
|
form.value.legalPhoto.push(res.result);
|
||||||
|
}
|
||||||
|
// 上传失败
|
||||||
|
function uploadErr () {
|
||||||
|
uploadLoading.value = false;
|
||||||
|
uploadLoading1.value = false;
|
||||||
|
}
|
||||||
|
// 上传失败回调
|
||||||
|
function handleFormatError (file) {
|
||||||
|
uploadLoading.value = false;
|
||||||
|
uploadLoading1.value = false;
|
||||||
|
Notice.warning({
|
||||||
|
title: 'The file format is incorrect',
|
||||||
|
desc: '上传文件格式不正确'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 上传大小限制
|
||||||
|
function handleMaxSize (file) {
|
||||||
|
uploadLoading.value = false;
|
||||||
|
uploadLoading1.value = false;
|
||||||
|
Notice.warning({
|
||||||
|
title: 'Exceeding file size limit',
|
||||||
|
desc: '文件大小不能超过2M'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 图片查看
|
||||||
|
function handleView (item) {
|
||||||
|
previewPicture.value = item;
|
||||||
|
visible.value = true;
|
||||||
|
}
|
||||||
|
// 删除图片
|
||||||
|
function handleRemove (index, listName) {
|
||||||
|
form.value[listName].splice(index, 1);
|
||||||
|
}
|
||||||
|
function normalizeLicencePhoto (value) {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value.length ? value[0] : '';
|
||||||
|
}
|
||||||
|
return value ? String(value).split(',').filter(Boolean)[0] || '' : '';
|
||||||
|
}
|
||||||
|
function initFormFromContent(content) {
|
||||||
|
if (!content || !Object.keys(content).length) return;
|
||||||
|
form.value = JSON.parse(JSON.stringify(content));
|
||||||
|
form.value.legalPhoto = content.legalPhoto
|
||||||
|
? String(content.legalPhoto).split(',').filter(Boolean)
|
||||||
|
: [];
|
||||||
|
form.value.licencePhoto = content.licencePhoto
|
||||||
|
? String(content.licencePhoto).split(',').filter(Boolean).slice(0, 1)
|
||||||
|
: [];
|
||||||
|
contentInitialized.value = true;
|
||||||
|
}
|
||||||
|
function restoreDraft() {
|
||||||
|
const draft = sessionStorage.getItem(FIRST_APPLY_DRAFT_KEY);
|
||||||
|
if (!draft) return;
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(draft);
|
||||||
|
form.value = {
|
||||||
|
legalPhoto: [],
|
||||||
|
licencePhoto: [],
|
||||||
|
...parsed,
|
||||||
|
};
|
||||||
|
form.value.legalPhoto = Array.isArray(parsed.legalPhoto) ? parsed.legalPhoto : [];
|
||||||
|
form.value.licencePhoto = Array.isArray(parsed.licencePhoto)
|
||||||
|
? parsed.licencePhoto.slice(0, 1)
|
||||||
|
: [];
|
||||||
|
} catch (e) {
|
||||||
|
sessionStorage.removeItem(FIRST_APPLY_DRAFT_KEY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function saveDraft() {
|
||||||
|
sessionStorage.setItem(FIRST_APPLY_DRAFT_KEY, JSON.stringify(form.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.content,
|
||||||
|
(val) => {
|
||||||
|
if (contentInitialized.value || !val || !Object.keys(val).length) return;
|
||||||
|
initFormFromContent(val);
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
accessToken.accessToken = storage.getItem('accessToken');
|
||||||
|
if (Object.keys(props.content).length) {
|
||||||
|
initFormFromContent(props.content);
|
||||||
|
} else {
|
||||||
|
restoreDraft();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
saveDraft();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
h4 {
|
h4 {
|
||||||
|
|||||||
@@ -40,60 +40,58 @@
|
|||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
|
import { onMounted, reactive, ref } from 'vue';
|
||||||
import { applySecond } from '@/api/shopentry';
|
import { applySecond } from '@/api/shopentry';
|
||||||
export default {
|
|
||||||
emits: ['change'],
|
const emit = defineEmits(['change']);
|
||||||
props: {
|
const props = defineProps({
|
||||||
content: {
|
content: {
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
type: Object
|
type: Object
|
||||||
}
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
loading: false, // 加载状态
|
|
||||||
form: {}, // 表单数据
|
|
||||||
rules: { // 验证规则
|
|
||||||
settlementBankAccountName: [
|
|
||||||
{ required: true, message: '请填写银行开户名称' }
|
|
||||||
],
|
|
||||||
settlementBankAccountNum: [
|
|
||||||
{ required: true, message: '请填写银行账号' }
|
|
||||||
],
|
|
||||||
settlementBankBranchName: [
|
|
||||||
{ required: true, message: '请填写开户银行支行名称' }
|
|
||||||
],
|
|
||||||
settlementBankJointName: [
|
|
||||||
{ required: true, message: '请填写支行联行号' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// 下一步
|
|
||||||
next () {
|
|
||||||
this.$refs.secondForm.validate((valid) => {
|
|
||||||
if (valid) {
|
|
||||||
this.loading = true;
|
|
||||||
applySecond(this.form)
|
|
||||||
.then((res) => {
|
|
||||||
this.loading = false;
|
|
||||||
if (res.success) this.$emit('change', 2);
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
this.loading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
if (this.content && Object.keys(this.content).length) {
|
|
||||||
this.form = JSON.parse(JSON.stringify(this.content));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const secondForm = ref(null);
|
||||||
|
const loading = ref(false); // 加载状态
|
||||||
|
const form = reactive({}); // 表单数据
|
||||||
|
const rules = { // 验证规则
|
||||||
|
settlementBankAccountName: [
|
||||||
|
{ required: true, message: '请填写银行开户名称' }
|
||||||
|
],
|
||||||
|
settlementBankAccountNum: [
|
||||||
|
{ required: true, message: '请填写银行账号' }
|
||||||
|
],
|
||||||
|
settlementBankBranchName: [
|
||||||
|
{ required: true, message: '请填写开户银行支行名称' }
|
||||||
|
],
|
||||||
|
settlementBankJointName: [
|
||||||
|
{ required: true, message: '请填写支行联行号' }
|
||||||
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 下一步
|
||||||
|
function next() {
|
||||||
|
secondForm.value.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
loading.value = true;
|
||||||
|
applySecond(form)
|
||||||
|
.then((res) => {
|
||||||
|
loading.value = false;
|
||||||
|
if (res.success) emit('change', 2);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (props.content && Object.keys(props.content).length) {
|
||||||
|
Object.assign(form, JSON.parse(JSON.stringify(props.content)));
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
h4 {
|
h4 {
|
||||||
|
|||||||
@@ -56,139 +56,134 @@
|
|||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
|
import { onMounted, reactive, ref } from "vue";
|
||||||
import { agreement, applyStatus } from "@/api/shopentry";
|
import { agreement, applyStatus } from "@/api/shopentry";
|
||||||
import firstApply from "./FirstApply";
|
import firstApply from "./FirstApply";
|
||||||
import secondApply from "./SecondApply";
|
import secondApply from "./SecondApply";
|
||||||
import thirdApply from "./ThirdApply";
|
import thirdApply from "./ThirdApply";
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
firstApply,
|
|
||||||
secondApply,
|
|
||||||
thirdApply,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
currentIndex: 0, // 当前步骤
|
|
||||||
showAgreement: false, // 协议显示
|
|
||||||
agreementCon: "", // 协议内容
|
|
||||||
checked: false, // 选中协议
|
|
||||||
firstData: {}, // 第一步数据
|
|
||||||
secondData: {}, // 第二步数据
|
|
||||||
thirdData: {}, // 第三步数据
|
|
||||||
storeDisable: "", // APPLY OPEN 开店中 CLOSED 关闭 REFUSED 拒绝 APPLYING 申请中,审核
|
|
||||||
initLoading: true,
|
|
||||||
agreementAccepted: false, // 是否已同意入驻协议
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
confirmAgreement() {
|
|
||||||
this.agreementAccepted = true;
|
|
||||||
this.showAgreement = false;
|
|
||||||
sessionStorage.setItem("shopEntryAgreementAccepted", "1");
|
|
||||||
},
|
|
||||||
assignApplyData(data) {
|
|
||||||
const first = [
|
|
||||||
"companyAddressPath",
|
|
||||||
"companyAddress",
|
|
||||||
"companyAddressIdPath",
|
|
||||||
"companyEmail",
|
|
||||||
"companyName",
|
|
||||||
"employeeNum",
|
|
||||||
"companyPhone",
|
|
||||||
"legalId",
|
|
||||||
"legalName",
|
|
||||||
"licencePhoto",
|
|
||||||
"legalPhoto",
|
|
||||||
"licenseNum",
|
|
||||||
"linkName",
|
|
||||||
"linkPhone",
|
|
||||||
"registeredCapital",
|
|
||||||
"scope",
|
|
||||||
];
|
|
||||||
const second = [
|
|
||||||
"settlementBankAccountName",
|
|
||||||
"settlementBankAccountNum",
|
|
||||||
"settlementBankBranchName",
|
|
||||||
"settlementBankJointName",
|
|
||||||
];
|
|
||||||
const third = [
|
|
||||||
"goodsManagementCategory",
|
|
||||||
"storeCenter",
|
|
||||||
"storeDesc",
|
|
||||||
"storeLogo",
|
|
||||||
"storeName",
|
|
||||||
"storeAddressIdPath",
|
|
||||||
"storeAddressPath",
|
|
||||||
"storeAddressDetail",
|
|
||||||
];
|
|
||||||
|
|
||||||
this.storeDisable = data.storeDisable || "";
|
const currentIndex = ref(0); // 当前步骤
|
||||||
Object.assign(this.firstData, Object.fromEntries(first.map((key) => [key, data[key]])));
|
const showAgreement = ref(false); // 协议显示
|
||||||
Object.assign(this.secondData, Object.fromEntries(second.map((key) => [key, data[key]])));
|
const agreementCon = ref(""); // 协议内容
|
||||||
Object.assign(this.thirdData, Object.fromEntries(third.map((key) => [key, data[key]])));
|
const checked = ref(false); // 选中协议
|
||||||
},
|
const firstData = reactive({}); // 第一步数据
|
||||||
getArticle() {
|
const secondData = reactive({}); // 第二步数据
|
||||||
// 入驻协议:库中无 STORE_REGISTER 文章时 result 为 null,不能直接读 content
|
const thirdData = reactive({}); // 第三步数据
|
||||||
agreement().then((res) => {
|
const storeDisable = ref(""); // APPLY OPEN 开店中 CLOSED 关闭 REFUSED 拒绝 APPLYING 申请中,审核
|
||||||
this.agreementCon = res.result?.content || "";
|
const initLoading = ref(true);
|
||||||
});
|
const agreementAccepted = ref(false); // 是否已同意入驻协议
|
||||||
},
|
|
||||||
getData(status) {
|
|
||||||
applyStatus().then((res) => {
|
|
||||||
if (!res.success) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!res.result) {
|
|
||||||
if (status === "init" && !this.agreementAccepted) {
|
|
||||||
this.showAgreement = true;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.assignApplyData(res.result);
|
function confirmAgreement() {
|
||||||
if (status === "init") {
|
agreementAccepted.value = true;
|
||||||
this.agreementAccepted = true;
|
showAgreement.value = false;
|
||||||
sessionStorage.setItem("shopEntryAgreementAccepted", "1");
|
sessionStorage.setItem("shopEntryAgreementAccepted", "1");
|
||||||
if (this.storeDisable === "APPLY") {
|
}
|
||||||
this.currentIndex = 0;
|
|
||||||
} else {
|
function assignApplyData(data) {
|
||||||
this.currentIndex = 3;
|
const first = [
|
||||||
}
|
"companyAddressPath",
|
||||||
}
|
"companyAddress",
|
||||||
}).finally(() => {
|
"companyAddressIdPath",
|
||||||
if (status === "init") {
|
"companyEmail",
|
||||||
this.initLoading = false;
|
"companyName",
|
||||||
}
|
"employeeNum",
|
||||||
});
|
"companyPhone",
|
||||||
},
|
"legalId",
|
||||||
refreshApplyData() {
|
"legalName",
|
||||||
return applyStatus().then((res) => {
|
"licencePhoto",
|
||||||
if (res.success && res.result) {
|
"legalPhoto",
|
||||||
this.assignApplyData(res.result);
|
"licenseNum",
|
||||||
}
|
"linkName",
|
||||||
}).catch((err) => {
|
"linkPhone",
|
||||||
console.warn('获取入驻申请状态失败', err);
|
"registeredCapital",
|
||||||
});
|
"scope",
|
||||||
},
|
];
|
||||||
// 下一步:先拉最新申请数据,再切步骤,避免第三步挂载后再改 content 触发校验
|
const second = [
|
||||||
async nextPage(step) {
|
"settlementBankAccountName",
|
||||||
if (typeof step !== "number") return;
|
"settlementBankAccountNum",
|
||||||
if (step > 0 && step <= 3) {
|
"settlementBankBranchName",
|
||||||
await this.refreshApplyData();
|
"settlementBankJointName",
|
||||||
|
];
|
||||||
|
const third = [
|
||||||
|
"goodsManagementCategory",
|
||||||
|
"storeCenter",
|
||||||
|
"storeDesc",
|
||||||
|
"storeLogo",
|
||||||
|
"storeName",
|
||||||
|
"storeAddressIdPath",
|
||||||
|
"storeAddressPath",
|
||||||
|
"storeAddressDetail",
|
||||||
|
];
|
||||||
|
|
||||||
|
storeDisable.value = data.storeDisable || "";
|
||||||
|
Object.assign(firstData, Object.fromEntries(first.map((key) => [key, data[key]])));
|
||||||
|
Object.assign(secondData, Object.fromEntries(second.map((key) => [key, data[key]])));
|
||||||
|
Object.assign(thirdData, Object.fromEntries(third.map((key) => [key, data[key]])));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getArticle() {
|
||||||
|
// 入驻协议:库中无 STORE_REGISTER 文章时 result 为 null,不能直接读 content
|
||||||
|
agreement().then((res) => {
|
||||||
|
agreementCon.value = res.result?.content || "";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getData(status) {
|
||||||
|
applyStatus().then((res) => {
|
||||||
|
if (!res.success) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!res.result) {
|
||||||
|
if (status === "init" && !agreementAccepted.value) {
|
||||||
|
showAgreement.value = true;
|
||||||
}
|
}
|
||||||
this.currentIndex = step;
|
return;
|
||||||
},
|
}
|
||||||
},
|
|
||||||
created() {
|
assignApplyData(res.result);
|
||||||
this.agreementAccepted = sessionStorage.getItem("shopEntryAgreementAccepted") === "1";
|
if (status === "init") {
|
||||||
},
|
agreementAccepted.value = true;
|
||||||
mounted() {
|
sessionStorage.setItem("shopEntryAgreementAccepted", "1");
|
||||||
this.getData("init");
|
if (storeDisable.value === "APPLY") {
|
||||||
this.getArticle();
|
currentIndex.value = 0;
|
||||||
},
|
} else {
|
||||||
};
|
currentIndex.value = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
if (status === "init") {
|
||||||
|
initLoading.value = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshApplyData() {
|
||||||
|
return applyStatus().then((res) => {
|
||||||
|
if (res.success && res.result) {
|
||||||
|
assignApplyData(res.result);
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
console.warn('获取入驻申请状态失败', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下一步:先拉最新申请数据,再切步骤,避免第三步挂载后再改 content 触发校验
|
||||||
|
async function nextPage(step) {
|
||||||
|
if (typeof step !== "number") return;
|
||||||
|
if (step > 0 && step <= 3) {
|
||||||
|
await refreshApplyData();
|
||||||
|
}
|
||||||
|
currentIndex.value = step;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化(原 created 钩子逻辑)
|
||||||
|
agreementAccepted.value = sessionStorage.getItem("shopEntryAgreementAccepted") === "1";
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getData("init");
|
||||||
|
getArticle();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.content {
|
.content {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="person-msg">
|
<div class="person-msg">
|
||||||
<el-form ref="thirdForm" :model="form" :rules="rules" label-width="140px">
|
<el-form ref="thirdFormRef" :model="form" :rules="rules" label-width="140px">
|
||||||
<h4>基础信息</h4>
|
<h4>基础信息</h4>
|
||||||
<el-form-item prop="storeName" label="店铺名称">
|
<el-form-item prop="storeName" label="店铺名称">
|
||||||
<el-input
|
<el-input
|
||||||
@@ -92,7 +92,8 @@
|
|||||||
<multipleMap ref="map" @callback="getAddress" />
|
<multipleMap ref="map" @callback="getAddress" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
|
import { onMounted, reactive, ref } from "vue";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
import { Delete, View } from '@element-plus/icons-vue';
|
import { Delete, View } from '@element-plus/icons-vue';
|
||||||
import { applyThird } from '@/api/shopentry';
|
import { applyThird } from '@/api/shopentry';
|
||||||
@@ -105,188 +106,184 @@ import { buyerUrl } from '@/plugins/request.js';
|
|||||||
import multipleMap from "@/components/map/multiple-map";
|
import multipleMap from "@/components/map/multiple-map";
|
||||||
|
|
||||||
|
|
||||||
export default {
|
const emit = defineEmits(['change']);
|
||||||
emits: ['change'],
|
|
||||||
props: {
|
|
||||||
content: {
|
|
||||||
default: () => ({}),
|
|
||||||
type: Object
|
|
||||||
}
|
|
||||||
},
|
|
||||||
components: { multipleMap, View, Delete },
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
loading: false, // 加载状态
|
|
||||||
uploadLoading: false, // 上传加载状态
|
|
||||||
action: buyerUrl + '/buyer/common/upload/file', // 上传地址
|
|
||||||
accessToken: {}, // 验证token
|
|
||||||
previewPicture: '', // 预览图片
|
|
||||||
|
|
||||||
visible: false, // 图片预览
|
const props = defineProps({
|
||||||
form: { // 表单数据
|
content: {
|
||||||
storeLogo: [],
|
default: () => ({}),
|
||||||
goodsManagementCategory: [],
|
type: Object
|
||||||
storeName: "",
|
|
||||||
storeAddressDetail: "",
|
|
||||||
storeDesc: "",
|
|
||||||
storeAddressIdPath: "",
|
|
||||||
storeAddressPath: "",
|
|
||||||
storeCenter: "",
|
|
||||||
},
|
|
||||||
rules: { // 验证规则
|
|
||||||
goodsManagementCategory: [
|
|
||||||
{ required: true, message: '请选择店铺经营类目' }
|
|
||||||
],
|
|
||||||
storeName: [{ required: true, message: '请填写店铺名称' }],
|
|
||||||
storeLogo: [{ required: true, message: '请上传店铺logo' }],
|
|
||||||
storeDesc: [{ required: true, message: '请填写店铺简介' }],
|
|
||||||
storeCenter: [{ required: true, message: '请选择店铺位置' }],
|
|
||||||
storeAddressIdPath: [{ required: true, message: '请选择店铺位置' }],
|
|
||||||
storeAddressDetail: [{ required: true, message: '请输入店铺详细地址' }]
|
|
||||||
},
|
|
||||||
categoryList: [] // 分类数据
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// 下一步
|
|
||||||
next () {
|
|
||||||
this.$refs.thirdForm.validate((valid) => {
|
|
||||||
if (valid) {
|
|
||||||
this.loading = true;
|
|
||||||
let params = JSON.parse(JSON.stringify(this.form));
|
|
||||||
params.storeLogo = this.form.storeLogo.toString();
|
|
||||||
params.goodsManagementCategory = this.form.goodsManagementCategory.toString();
|
|
||||||
applyThird(params)
|
|
||||||
.then((res) => {
|
|
||||||
this.loading = false;
|
|
||||||
if (res.success) this.$emit('change', 3);
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
this.loading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 上传之前
|
|
||||||
beforeUpload (file) {
|
|
||||||
this.uploadLoading = true;
|
|
||||||
if (this.form.storeLogo.length >= 1) {
|
|
||||||
Message.warning('最多上传一张图片')
|
|
||||||
this.uploadLoading = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return this.validateUploadFile(file);
|
|
||||||
},
|
|
||||||
validateUploadFile (file) {
|
|
||||||
const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'];
|
|
||||||
const isAllowedType = allowedTypes.includes(file.type);
|
|
||||||
const isLt2M = file.size / 1024 / 1024 < 2;
|
|
||||||
if (!isAllowedType) {
|
|
||||||
this.uploadLoading = false;
|
|
||||||
Message.warning('上传文件格式不正确');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!isLt2M) {
|
|
||||||
this.uploadLoading = false;
|
|
||||||
Message.warning('文件大小不能超过2M');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
// 上传成功回调
|
|
||||||
handleSuccess (res, file) {
|
|
||||||
this.uploadLoading = false;
|
|
||||||
this.form.storeLogo = [res.result];
|
|
||||||
},
|
|
||||||
// 上传失败
|
|
||||||
uploadErr () {
|
|
||||||
this.uploadLoading = false;
|
|
||||||
},
|
|
||||||
// 查看图片
|
|
||||||
handleView (item) {
|
|
||||||
this.previewPicture = item;
|
|
||||||
this.visible = true;
|
|
||||||
},
|
|
||||||
// 删除图片
|
|
||||||
handleRemove (index, listName) {
|
|
||||||
this.form[listName].splice(index, 1);
|
|
||||||
},
|
|
||||||
// 选择坐标回调
|
|
||||||
getAddress (val) {
|
|
||||||
if(val.type === 'select'){
|
|
||||||
const paths = val.data.map(item => item.name).join(',')
|
|
||||||
const ids = val.data.map(item => item.id).join(',')
|
|
||||||
this.form["storeAddressPath"] = paths;
|
|
||||||
this.form["storeAddressIdPath"] = ids;
|
|
||||||
this.form.storeCenter = val.data[val.data.length - 1].center
|
|
||||||
}else{
|
|
||||||
this.form["storeAddressPath"] = val.data.addr;
|
|
||||||
this.form["storeAddressIdPath"] = val.data.addrId;
|
|
||||||
|
|
||||||
this.form['storeCenter'] = val.data.position.lng + ',' + val.data.position.lat
|
|
||||||
;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 获取商品分类
|
|
||||||
getCategoryList () {
|
|
||||||
getCategory(0).then((res) => {
|
|
||||||
if (res.success) {
|
|
||||||
this.categoryList = res.result || [];
|
|
||||||
this.syncCategorySelection();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
normalizeCategoryId (id) {
|
|
||||||
return id == null ? '' : String(id);
|
|
||||||
},
|
|
||||||
syncCategorySelection () {
|
|
||||||
if (!Array.isArray(this.form.goodsManagementCategory)) {
|
|
||||||
this.form.goodsManagementCategory = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.form.goodsManagementCategory = this.form.goodsManagementCategory.map(
|
|
||||||
(id) => this.normalizeCategoryId(id)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
createEmptyForm () {
|
|
||||||
return {
|
|
||||||
storeLogo: [],
|
|
||||||
goodsManagementCategory: [],
|
|
||||||
storeName: "",
|
|
||||||
storeAddressDetail: "",
|
|
||||||
storeDesc: "",
|
|
||||||
storeAddressIdPath: "",
|
|
||||||
storeAddressPath: "",
|
|
||||||
storeCenter: "",
|
|
||||||
};
|
|
||||||
},
|
|
||||||
initFormFromContent (content) {
|
|
||||||
const src = content && Object.keys(content).length
|
|
||||||
? JSON.parse(JSON.stringify(content))
|
|
||||||
: {};
|
|
||||||
this.form = {
|
|
||||||
...this.createEmptyForm(),
|
|
||||||
...src,
|
|
||||||
};
|
|
||||||
this.form.storeLogo = src.storeLogo
|
|
||||||
? String(src.storeLogo).split(",").filter(Boolean)
|
|
||||||
: [];
|
|
||||||
this.form.goodsManagementCategory = src.goodsManagementCategory
|
|
||||||
? String(src.goodsManagementCategory).split(",").filter(Boolean)
|
|
||||||
: [];
|
|
||||||
["storeName", "storeAddressDetail", "storeDesc", "storeAddressIdPath", "storeAddressPath", "storeCenter"].forEach((key) => {
|
|
||||||
if (this.form[key] == null) this.form[key] = "";
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
created () {
|
|
||||||
this.initFormFromContent(this.content);
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
this.accessToken.accessToken = storage.getItem('accessToken');
|
|
||||||
this.getCategoryList();
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const thirdFormRef = ref(null);
|
||||||
|
|
||||||
|
const loading = ref(false); // 加载状态
|
||||||
|
const uploadLoading = ref(false); // 上传加载状态
|
||||||
|
const action = buyerUrl + '/buyer/common/upload/file'; // 上传地址
|
||||||
|
const accessToken = reactive({}); // 验证token
|
||||||
|
const previewPicture = ref(''); // 预览图片
|
||||||
|
|
||||||
|
const visible = ref(false); // 图片预览
|
||||||
|
const form = ref({ // 表单数据
|
||||||
|
storeLogo: [],
|
||||||
|
goodsManagementCategory: [],
|
||||||
|
storeName: "",
|
||||||
|
storeAddressDetail: "",
|
||||||
|
storeDesc: "",
|
||||||
|
storeAddressIdPath: "",
|
||||||
|
storeAddressPath: "",
|
||||||
|
storeCenter: "",
|
||||||
|
});
|
||||||
|
const rules = { // 验证规则
|
||||||
|
goodsManagementCategory: [
|
||||||
|
{ required: true, message: '请选择店铺经营类目' }
|
||||||
|
],
|
||||||
|
storeName: [{ required: true, message: '请填写店铺名称' }],
|
||||||
|
storeLogo: [{ required: true, message: '请上传店铺logo' }],
|
||||||
|
storeDesc: [{ required: true, message: '请填写店铺简介' }],
|
||||||
|
storeCenter: [{ required: true, message: '请选择店铺位置' }],
|
||||||
|
storeAddressIdPath: [{ required: true, message: '请选择店铺位置' }],
|
||||||
|
storeAddressDetail: [{ required: true, message: '请输入店铺详细地址' }]
|
||||||
};
|
};
|
||||||
|
const categoryList = ref([]); // 分类数据
|
||||||
|
|
||||||
|
// 下一步
|
||||||
|
function next () {
|
||||||
|
thirdFormRef.value.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
loading.value = true;
|
||||||
|
let params = JSON.parse(JSON.stringify(form.value));
|
||||||
|
params.storeLogo = form.value.storeLogo.toString();
|
||||||
|
params.goodsManagementCategory = form.value.goodsManagementCategory.toString();
|
||||||
|
applyThird(params)
|
||||||
|
.then((res) => {
|
||||||
|
loading.value = false;
|
||||||
|
if (res.success) emit('change', 3);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 上传之前
|
||||||
|
function beforeUpload (file) {
|
||||||
|
uploadLoading.value = true;
|
||||||
|
if (form.value.storeLogo.length >= 1) {
|
||||||
|
Message.warning('最多上传一张图片')
|
||||||
|
uploadLoading.value = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return validateUploadFile(file);
|
||||||
|
}
|
||||||
|
function validateUploadFile (file) {
|
||||||
|
const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'];
|
||||||
|
const isAllowedType = allowedTypes.includes(file.type);
|
||||||
|
const isLt2M = file.size / 1024 / 1024 < 2;
|
||||||
|
if (!isAllowedType) {
|
||||||
|
uploadLoading.value = false;
|
||||||
|
Message.warning('上传文件格式不正确');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!isLt2M) {
|
||||||
|
uploadLoading.value = false;
|
||||||
|
Message.warning('文件大小不能超过2M');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 上传成功回调
|
||||||
|
function handleSuccess (res, file) {
|
||||||
|
uploadLoading.value = false;
|
||||||
|
form.value.storeLogo = [res.result];
|
||||||
|
}
|
||||||
|
// 上传失败
|
||||||
|
function uploadErr () {
|
||||||
|
uploadLoading.value = false;
|
||||||
|
}
|
||||||
|
// 查看图片
|
||||||
|
function handleView (item) {
|
||||||
|
previewPicture.value = item;
|
||||||
|
visible.value = true;
|
||||||
|
}
|
||||||
|
// 删除图片
|
||||||
|
function handleRemove (index, listName) {
|
||||||
|
form.value[listName].splice(index, 1);
|
||||||
|
}
|
||||||
|
// 选择坐标回调
|
||||||
|
function getAddress (val) {
|
||||||
|
if(val.type === 'select'){
|
||||||
|
const paths = val.data.map(item => item.name).join(',')
|
||||||
|
const ids = val.data.map(item => item.id).join(',')
|
||||||
|
form.value["storeAddressPath"] = paths;
|
||||||
|
form.value["storeAddressIdPath"] = ids;
|
||||||
|
form.value.storeCenter = val.data[val.data.length - 1].center
|
||||||
|
}else{
|
||||||
|
form.value["storeAddressPath"] = val.data.addr;
|
||||||
|
form.value["storeAddressIdPath"] = val.data.addrId;
|
||||||
|
|
||||||
|
form.value['storeCenter'] = val.data.position.lng + ',' + val.data.position.lat
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 获取商品分类
|
||||||
|
function getCategoryList () {
|
||||||
|
getCategory(0).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
categoryList.value = res.result || [];
|
||||||
|
syncCategorySelection();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function normalizeCategoryId (id) {
|
||||||
|
return id == null ? '' : String(id);
|
||||||
|
}
|
||||||
|
function syncCategorySelection () {
|
||||||
|
if (!Array.isArray(form.value.goodsManagementCategory)) {
|
||||||
|
form.value.goodsManagementCategory = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
form.value.goodsManagementCategory = form.value.goodsManagementCategory.map(
|
||||||
|
(id) => normalizeCategoryId(id)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
function createEmptyForm () {
|
||||||
|
return {
|
||||||
|
storeLogo: [],
|
||||||
|
goodsManagementCategory: [],
|
||||||
|
storeName: "",
|
||||||
|
storeAddressDetail: "",
|
||||||
|
storeDesc: "",
|
||||||
|
storeAddressIdPath: "",
|
||||||
|
storeAddressPath: "",
|
||||||
|
storeCenter: "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function initFormFromContent (content) {
|
||||||
|
const src = content && Object.keys(content).length
|
||||||
|
? JSON.parse(JSON.stringify(content))
|
||||||
|
: {};
|
||||||
|
form.value = {
|
||||||
|
...createEmptyForm(),
|
||||||
|
...src,
|
||||||
|
};
|
||||||
|
form.value.storeLogo = src.storeLogo
|
||||||
|
? String(src.storeLogo).split(",").filter(Boolean)
|
||||||
|
: [];
|
||||||
|
form.value.goodsManagementCategory = src.goodsManagementCategory
|
||||||
|
? String(src.goodsManagementCategory).split(",").filter(Boolean)
|
||||||
|
: [];
|
||||||
|
["storeName", "storeAddressDetail", "storeDesc", "storeAddressIdPath", "storeAddressPath", "storeCenter"].forEach((key) => {
|
||||||
|
if (form.value[key] == null) form.value[key] = "";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
initFormFromContent(props.content);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
accessToken.accessToken = storage.getItem('accessToken');
|
||||||
|
getCategoryList();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
h4 {
|
h4 {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user