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:
lifenlong
2026-10-13 07:06:59 +08:00
parent 870bacdd8c
commit 88fc1b0b2a
674 changed files with 71220 additions and 69943 deletions

View File

@@ -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 |

View File

@@ -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 模板替换

View File

@@ -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']],
}, },
}, },
] ]

View File

@@ -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": [

View File

@@ -4,26 +4,28 @@
</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);
this.$store.commit("SET_SITENAME", data.siteName); appStore.setSiteName(data.siteName);
this.$store.commit("SET_LOGOIMG", data.buyerSideLogo); appStore.setLogoImg(data.buyerSideLogo);
if (this.$route.meta.title === undefined) { if (route.meta.title === undefined) {
window.document.title = data.siteName; window.document.title = data.siteName;
} }
let link = let link =
@@ -33,27 +35,29 @@ export default {
link.href = data.buyerSideIcon; link.href = data.buyerSideIcon;
link.rel = "shortcut icon"; link.rel = "shortcut icon";
document.getElementsByTagName("head")[0].appendChild(link); document.getElementsByTagName("head")[0].appendChild(link);
}, }
init() {
function init() {
const expirationTime = storage.getItem("sitelogo_expiration_time"); const expirationTime = storage.getItem("sitelogo_expiration_time");
const cacheValid = const cacheValid =
expirationTime && new Date() <= new Date(Number(expirationTime)); expirationTime && new Date() <= new Date(Number(expirationTime));
if (cacheValid) { if (cacheValid) {
this.applySiteInfo({ applySiteInfo({
siteName: storage.getItem("siteName"), siteName: storage.getItem("siteName"),
buyerSideLogo: storage.getItem("logoImg"), buyerSideLogo: storage.getItem("logoImg"),
buyerSideIcon: storage.getItem("siteIcon"), buyerSideIcon: storage.getItem("siteIcon"),
}); });
} }
this.getSite(); getSite();
this.getTheme(); getTheme();
}, }
getSite() {
function getSite() {
getBaseSite().then((res) => { getBaseSite().then((res) => {
if (res.success && res.result.settingValue) { if (res.success && res.result.settingValue) {
try { try {
const data = JSON.parse(res.result.settingValue); const data = JSON.parse(res.result.settingValue);
this.applySiteInfo(data); applySiteInfo(data);
} catch { } catch {
// 站点配置解析失败时使用缓存 // 站点配置解析失败时使用缓存
} }
@@ -61,14 +65,17 @@ export default {
}).catch((err) => { }).catch((err) => {
console.warn('获取站点配置失败', err); console.warn('获取站点配置失败', err);
}); });
}, }
getTheme() {
function getTheme() {
fetchAndApplyTheme(getThemeSetting).catch((err) => { fetchAndApplyTheme(getThemeSetting).catch((err) => {
console.warn('获取主题配置失败', err); console.warn('获取主题配置失败', err);
}); });
}, }
},
}; onMounted(() => {
init();
});
</script> </script>
<style lang="scss"> <style lang="scss">
#app { #app {

View File

@@ -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,88 +146,90 @@ 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, const logoImg = computed(() => appStore.logoImg)
default: true
}, const trimmedKeyword = computed(() => {
storeId: { return (searchData.value || '').trim()
type: String, })
default: ""
}, const promotionTags = computed(() => {
hover: { return parseHotWords(appStore.hotWordsList)
type: Boolean, })
default: false
}, const showPanel = computed(() => {
useClass:{ if (!panelVisible.value) return false
type:null, if (trimmedKeyword.value) return true
default:'' return history.value.length > 0 || promotionTags.value.length > 0
})
watch(
() => route.query.keyword,
(value) => {
searchData.value = value || ''
} }
}, )
data() {
return { function selectTags(item) {
searchData: '', searchData.value = item
history: [], search()
panelVisible: false, }
suggestions: [],
suggesting: false, function remember(keyword) {
suggestTimer: null, const next = [keyword, ...history.value.filter((item) => item !== keyword)].slice(0, HISTORY_LIMIT)
suggestSeq: 0, history.value = next
};
},
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)) localStorage.setItem(HISTORY_KEY, JSON.stringify(next))
}, }
clearHistory() {
this.history = [] function clearHistory() {
history.value = []
localStorage.removeItem(HISTORY_KEY) localStorage.removeItem(HISTORY_KEY)
}, }
openPanel() {
this.panelVisible = true function openPanel() {
if (this.trimmedKeyword) this.queueSuggest() panelVisible.value = true
}, if (trimmedKeyword.value) queueSuggest()
closePanel() { }
this.panelVisible = false
}, function closePanel() {
onDocumentClick(event) { panelVisible.value = false
if (this.$el && !this.$el.contains(event.target)) this.closePanel() }
},
queueSuggest() { function onDocumentClick(event) {
this.panelVisible = true if (rootRef.value && !rootRef.value.contains(event.target)) closePanel()
clearTimeout(this.suggestTimer) }
const keyword = this.trimmedKeyword
function queueSuggest() {
panelVisible.value = true
clearTimeout(suggestTimer)
const keyword = trimmedKeyword.value
if (!keyword) { if (!keyword) {
this.suggestions = [] suggestions.value = []
this.suggesting = false suggesting.value = false
return return
} }
this.suggesting = true suggesting.value = true
this.suggestTimer = setTimeout(() => this.fetchSuggest(keyword), 300) suggestTimer = setTimeout(() => fetchSuggest(keyword), 300)
}, }
fetchSuggest(keyword) {
const seq = ++this.suggestSeq function fetchSuggest(keyword) {
const seq = ++suggestSeq
const params = { keyword, pageNumber: 1, pageSize: 8 } const params = { keyword, pageNumber: 1, pageSize: 8 }
if (this.storeId) params.storeId = this.storeId if (props.storeId) params.storeId = props.storeId
goodsList(params).then((res) => { goodsList(params).then((res) => {
if (seq !== this.suggestSeq || keyword !== this.trimmedKeyword) return if (seq !== suggestSeq || keyword !== trimmedKeyword.value) return
const records = res && res.success ? (res.result.records || []) : [] const records = res && res.success ? (res.result.records || []) : []
const seen = new Set() const seen = new Set()
const names = [] const names = []
this.promotionTags.forEach((word) => { promotionTags.value.forEach((word) => {
if (word && String(word).toLowerCase().includes(keyword.toLowerCase()) && !seen.has(word)) { if (word && String(word).toLowerCase().includes(keyword.toLowerCase()) && !seen.has(word)) {
seen.add(word) seen.add(word)
names.push(word) names.push(word)
@@ -206,85 +242,70 @@ export default {
names.push(name) names.push(name)
} }
}) })
this.suggestions = names.slice(0, 8) suggestions.value = names.slice(0, 8)
}).catch(() => { }).catch(() => {
if (seq === this.suggestSeq) this.suggestions = [] if (seq === suggestSeq) suggestions.value = []
}).finally(() => { }).finally(() => {
if (seq === this.suggestSeq) this.suggesting = false if (seq === suggestSeq) suggesting.value = false
}) })
}, }
search () {
const keyword = this.trimmedKeyword function search() {
this.searchData = keyword const keyword = trimmedKeyword.value
this.closePanel() searchData.value = keyword
if (keyword) this.remember(keyword) closePanel()
const url = this.$route.path; if (keyword) remember(keyword)
if(url == '/goodsList'){ const url = route.path
this.$emit('search', keyword) if (url == '/goodsList') {
}else{ emit('search', keyword)
} else {
const pushData = { const pushData = {
path:'/goodsList', path: '/goodsList',
query: { keyword }, query: { keyword },
} }
if(this.storeId) pushData.query.storeId = this.storeId if (props.storeId) pushData.query.storeId = props.storeId
this.$router.push(pushData); router.push(pushData)
} }
}, }
searchStore() {
this.$emit('search', this.trimmedKeyword) function searchStore() {
}, emit('search', trimmedKeyword.value)
applyHotWords(result) { }
function applyHotWords(result) {
const list = parseHotWords(result) const list = parseHotWords(result)
this.$store.commit('SET_HOTWORDS', list) appStore.setHotWords(list)
storage.setItem('hotWordsList', JSON.stringify(list)) storage.setItem('hotWordsList', JSON.stringify(list))
}, }
loadHotWords() {
function loadHotWords() {
hotWords({ count: 5 }).then((res) => { hotWords({ count: 5 }).then((res) => {
if (res.success && res.result) this.applyHotWords(res.result) if (res.success && res.result) applyHotWords(res.result)
}).catch(() => { }).catch(() => {
// 热词为可选数据,失败时沿用缓存或默认热词 // 热词为可选数据,失败时沿用缓存或默认热词
}) })
storage.setItem('hotWordsReloadTime', new Date().getTime()) 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) { // 初始化(原 created 钩子逻辑)
searchData.value = route.query.keyword || ''
history.value = readHistory()
if (!props.hover) {
const reloadTime = storage.getItem('hotWordsReloadTime') const reloadTime = storage.getItem('hotWordsReloadTime')
const time = new Date().getTime() - 5 * 60 * 1000 const time = new Date().getTime() - 5 * 60 * 1000
if (!reloadTime || time > reloadTime) this.loadHotWords() if (!reloadTime || time > reloadTime) loadHotWords()
} }
},
mounted() { onMounted(() => {
this.onDocumentClick = this.onDocumentClick.bind(this) document.addEventListener('click', onDocumentClick)
document.addEventListener('click', this.onDocumentClick) })
},
beforeUnmount() { onUnmounted(() => {
clearTimeout(this.suggestTimer) clearTimeout(suggestTimer)
document.removeEventListener('click', this.onDocumentClick) document.removeEventListener('click', onDocumentClick)
} })
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.navbar { .navbar {

View File

@@ -45,21 +45,22 @@
</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) {
function parts(text) {
const value = String(text || ""); const value = String(text || "");
const key = this.keyword; const key = props.keyword;
if (!key) return [{ text: value, hit: false }]; if (!key) return [{ text: value, hit: false }];
const index = value.toLowerCase().indexOf(key.toLowerCase()); const index = value.toLowerCase().indexOf(key.toLowerCase());
if (index < 0) return [{ text: value, hit: false }]; if (index < 0) return [{ text: value, hit: false }];
@@ -68,9 +69,7 @@ export default {
{ text: value.slice(index, index + key.length), hit: true }, { text: value.slice(index, index + key.length), hit: true },
{ text: value.slice(index + key.length), hit: false }, { text: value.slice(index + key.length), hit: false },
].filter((part) => part.text); ].filter((part) => part.text);
}, }
},
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -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,21 +52,23 @@ import {
editMemberAddress, editMemberAddress,
getAddrDetail getAddrDetail
} from '@/api/address'; } from '@/api/address';
export default {
name: 'addressManage', defineOptions({ name: 'addressManage' });
props: {
const props = defineProps({
id: { // 传入的地址id id: { // 传入的地址id
defalut: '', defalut: '',
type: String type: String
} }
}, });
data () {
return { const emit = defineEmits(['change']);
showAddr: false, // 控制模态框显隐
formData: { // 表单数据 const showAddr = ref(false); // 控制模态框显隐
const formData = ref({ // 表单数据
isDefault: false isDefault: false
}, });
ruleInline: { // 验证规则 const ruleInline = { // 验证规则
name: [{ required: true, message: '请输入收件人姓名', trigger: 'blur' }], name: [{ required: true, message: '请输入收件人姓名', trigger: 'blur' }],
address: [{ required: true, message: '请输入地址', trigger: 'change' }], address: [{ required: true, message: '请输入地址', trigger: 'change' }],
detail: [ detail: [
@@ -80,93 +83,95 @@ export default {
trigger: 'blur' trigger: 'blur'
} }
] ]
}, };
loading: false, // 提交的加载状态 const loading = ref(false); // 提交的加载状态
mapMsg: {} // 地图信息 const mapMsg = ref({}); // 地图信息
};
}, const formRef = ref(null);
methods: { const mapRef = ref(null);
save () { // 保存地址
this.$refs.form.validate((valid) => { function save () { // 保存地址
formRef.value.validate((valid) => {
if (valid) { if (valid) {
const params = JSON.parse(JSON.stringify(this.formData)); const params = JSON.parse(JSON.stringify(formData.value));
params.consigneeAddressPath = params.address.replace(/\s/g, ','); params.consigneeAddressPath = params.address.replace(/\s/g, ',');
delete params.address; delete params.address;
this.loading = true; loading.value = true;
if (this.id) { if (props.id) {
editMemberAddress(params).then((res) => { editMemberAddress(params).then((res) => {
this.loading = false; loading.value = false;
if (res.success) { if (res.success) {
Message.success('编辑地址成功'); Message.success('编辑地址成功');
this.$emit('change', true); emit('change', true);
this.hide(); hide();
} }
}).catch(() => { this.loading = false; }); }).catch(() => { loading.value = false; });
} else { } else {
newMemberAddress(params).then((res) => { newMemberAddress(params).then((res) => {
this.loading = false; loading.value = false;
if (res.success) { if (res.success) {
Message.success('新增地址成功'); Message.success('新增地址成功');
this.$emit('change', true); emit('change', true);
this.hide(); hide();
} }
}).catch(() => { this.loading = false; }); }).catch(() => { loading.value = false; });
} }
} }
}); });
}, }
getAddrById (id) {
function getAddrById (id) {
// 获取地址详情 // 获取地址详情
getAddrDetail(id).then((res) => { getAddrDetail(id).then((res) => {
if (res.success) { if (res.success) {
const data = res.result; const data = res.result;
data.address = res.result.consigneeAddressPath.replace(/,/g, ' '); data.address = res.result.consigneeAddressPath.replace(/,/g, ' ');
this.formData = data; formData.value = data;
} }
}); });
}, }
getAddress (val) {
function getAddress (val) {
// 获取地图选择信息 // 获取地图选择信息
if(val.type === 'select'){ if(val.type === 'select'){
const paths = val.data.map(item => item.name).join(',') const paths = val.data.map(item => item.name).join(',')
const ids = val.data.map(item => item.id).join(',') const ids = val.data.map(item => item.id).join(',')
this.formData['address'] = paths formData.value['address'] = paths
this.formData['consigneeAddressIdPath'] = ids formData.value['consigneeAddressIdPath'] = ids
const coord = val.data[val.data.length - 1].center.split(',') const coord = val.data[val.data.length - 1].center.split(',')
this.formData.lat = coord[1] formData.value.lat = coord[1]
this.formData.lon = coord[0] formData.value.lon = coord[0]
}else{ }else{
this.formData["address"] = val.data.addr; formData.value["address"] = val.data.addr;
this.formData["consigneeAddressIdPath"] = val.data.addrId; formData.value["consigneeAddressIdPath"] = val.data.addrId;
this.formData["detail"] = val.data.address; formData.value["detail"] = val.data.address;
this.formData.lat = val.data.position.lat; formData.value.lat = val.data.position.lat;
this.formData.lon = val.data.position.lng; formData.value.lon = val.data.position.lng;
} }
}, }
show () { // 地址模态框显示
this.showAddr = true; function show () { // 地址模态框显示
}, showAddr.value = true;
hide () { // 地址模态框隐藏 }
this.showAddr = false;
} function hide () { // 地址模态框隐藏
}, showAddr.value = false;
watch: { }
id: { // 传入的地址id
handler: function (v) { defineExpose({ show, hide });
watch(
() => props.id, // 传入的地址id
(v) => {
if (v) { if (v) {
this.getAddrById(v); getAddrById(v);
} else { } else {
this.formData = {isDefault: false} formData.value = {isDefault: false}
this.$refs.form.resetFields(); formRef.value.resetFields();
} }
} }
} );
},
components: {
multipleMap
}
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -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 { const show = ref(true); // 是否显示头部广告
show: true // 是否显示头部广告
}
}
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.advertising { .advertising {

View File

@@ -26,10 +26,13 @@
</el-card> </el-card>
</template> </template>
<script> <script setup>
export default { import { ref, watch } from "vue";
name: "index", import { useRouter } from "vue-router";
props: {
defineOptions({ name: "index" });
const props = defineProps({
_Tabs: { _Tabs: {
type: null, type: null,
default: "", default: "",
@@ -54,29 +57,31 @@ export default {
type: Number, type: Number,
default: 0, default: 0,
}, },
}, });
data() {
return { const emit = defineEmits(["_Change"]);
isActive: this._ActiveTab,
}; const router = useRouter();
},
watch: { const isActive = ref(props._ActiveTab);
_ActiveTab(val) {
this.isActive = val; watch(
}, () => props._ActiveTab,
}, (val) => {
methods: { isActive.value = val;
callBack() {
if (this._Src) {
this.$router.push({ path: this._Src });
} }
}, );
tabsChange(index) {
this.isActive = index; function callBack() {
this.$emit("_Change", index); 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">

View File

@@ -78,51 +78,36 @@
</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' })
const props = defineProps({
title: { title: {
default: '', default: '',
type: String type: String
} }
}, })
watch: {
title (val) { const router = useRouter()
switch (val) { const appStore = useAppStore()
case '购物车':
this.getCartList() const loading = ref(false) // 控制spin显隐
break; const cartList = ref([]) // 购物车列表
case '我的订单': const couponList = ref([]) // 优惠券列表
this.getOrderList() const orderList = ref([]) // 订单列表
break; const collectList = ref([]) // 收藏列表
case '我的足迹': const tracksList = ref([]) // 足迹列表
this.getTracksList() const orderStatusList = ref([ // 订单状态
break;
case '优惠券':
this.getCouponList()
break;
case '我的收藏':
this.getCollectList()
break;
}
}
},
data () {
return {
loading: false, // 控制spin显隐
cartList: [], // 购物车列表
couponList: [], // 优惠券列表
orderList: [], // 订单列表
collectList: [], // 收藏列表
tracksList: [], // 足迹列表
orderStatusList: [ // 订单状态
{ {
name: '未付款', name: '未付款',
status: 'UNPAID' status: 'UNPAID'
@@ -151,37 +136,55 @@ export default {
name: '已取消', name: '已取消',
status: 'CANCELLED' status: 'CANCELLED'
} }
] ])
};
}, watch(() => props.title, (val) => {
methods: { switch (val) {
getCartList () { // 获取购物车列表 case '购物车':
this.loading = true getCartList()
break;
case '我的订单':
getOrderList()
break;
case '我的足迹':
getTracksList()
break;
case '优惠券':
getCouponList()
break;
case '我的收藏':
getCollectList()
break;
}
})
function getCartList () { // 获取购物车列表
loading.value = true
cartGoodsAll().then(res => { cartGoodsAll().then(res => {
this.loading = false loading.value = false
this.cartList = res.result.skuList cartList.value = res.result.skuList
}) })
}, }
// 删除商品 // 删除商品
delGoods (id) { function delGoods (id) {
delCartGoods({ skuIds: id }).then((res) => { delCartGoods({ skuIds: id }).then((res) => {
if (res.success) { if (res.success) {
Message.success('删除成功'); Message.success('删除成功');
this.getCartList(); getCartList();
cartCount().then(res => { cartCount().then(res => {
this.$store.commit('SET_CARTNUM', res.result) appStore.setCartNum(res.result)
this.Cookies.setItem('cartNum', res.result) storage.setItem('cartNum', res.result)
}) })
} else { } else {
Message.error(res.message); Message.error(res.message);
} }
}); });
}, }
filterOrderStatus (status) { // 获取订单状态中文 function filterOrderStatus (status) { // 获取订单状态中文
const ob = this.orderStatusList.filter(e => { return e.status === status }); const ob = orderStatusList.value.filter(e => { return e.status === status });
return ob[0].name return ob[0].name
}, }
receive (item) { // 领取优惠券 function receive (item) { // 领取优惠券
receiveCoupon(item.id).then(res => { receiveCoupon(item.id).then(res => {
if (res.success) { if (res.success) {
Modal.confirm({ Modal.confirm({
@@ -190,24 +193,24 @@ export default {
okText: '我的优惠券', okText: '我的优惠券',
cancelText: '立即使用', cancelText: '立即使用',
onOk: () => { onOk: () => {
this.$router.push('/home/Coupons') router.push('/home/Coupons')
}, },
onCancel: () => { onCancel: () => {
if (item.storeId !== '0') { if (item.storeId !== '0') {
this.$router.push({path: '/merchant', query: {id: item.storeId}}) router.push({path: '/merchant', query: {id: item.storeId}})
} else { } else {
if (item.scopeType === 'PORTION_GOODS_CATEGORY') { if (item.scopeType === 'PORTION_GOODS_CATEGORY') {
this.$router.push({path: '/goodsList', query: {categoryId: item.scopeId}}) router.push({path: '/goodsList', query: {categoryId: item.scopeId}})
} else { } else {
this.$router.push({path: '/goodsList'}) router.push({path: '/goodsList'})
} }
} }
} }
}); });
} }
}) })
}, }
useScope (type, storeName) { // 判断优惠券使用范围 function useScope (type, storeName) { // 判断优惠券使用范围
let shop = '平台'; let shop = '平台';
let goods = '全部商品' let goods = '全部商品'
if (storeName !== 'platform') shop = storeName if (storeName !== 'platform') shop = storeName
@@ -223,82 +226,80 @@ export default {
break; break;
} }
return `${shop}${goods}可用` return `${shop}${goods}可用`
}, }
addToCart (id) { // 添加商品到购物车 function addToCart (id) { // 添加商品到购物车
const params = { const params = {
num: 1, num: 1,
skuId: id skuId: id
} }
this.loading = true; loading.value = true;
addCartGoods(params).then(res => { addCartGoods(params).then(res => {
this.loading = false; loading.value = false;
if (res.success) { if (res.success) {
Message.success('商品已成功添加到购物车') Message.success('商品已成功添加到购物车')
} else { } else {
Message.warning(res.message); Message.warning(res.message);
} }
}).catch(() => { this.loading = false }); }).catch(() => { loading.value = false });
}, }
getCouponList () { // 获取优惠券列表 function getCouponList () { // 获取优惠券列表
// this.loading = true; // loading.value = true;
const params = { const params = {
pageNumber: 1, pageNumber: 1,
pageSize: 10 pageSize: 10
} }
couponList(params).then(res => { fetchCouponList(params).then(res => {
this.loading = false loading.value = false
if (res.success) { if (res.success) {
this.couponList = res.result.records couponList.value = res.result.records
} }
}).catch(() => { this.loading = false }) }).catch(() => { loading.value = false })
}, }
getOrderList () { // 获取订单列表 function getOrderList () { // 获取订单列表
this.loading = true loading.value = true
const params = { const params = {
pageNumber: 1, pageNumber: 1,
pageSize: 10, pageSize: 10,
tag: 'ALL' tag: 'ALL'
} }
getOrderList(params).then(res => { fetchOrderList(params).then(res => {
this.loading = false loading.value = false
if (res.success) { if (res.success) {
this.orderList = res.result.records; orderList.value = res.result.records;
} }
}); });
}, }
getCollectList () { // 获取收藏列表 function getCollectList () { // 获取收藏列表
const params = { const params = {
pageNumber: 1, pageNumber: 1,
pageSize: 10, pageSize: 10,
type: 'GOODS' type: 'GOODS'
} }
this.loading = true loading.value = true
collectList(params).then(res => { fetchCollectList(params).then(res => {
this.loading = false loading.value = false
this.collectList = res.result.records collectList.value = res.result.records
}) })
}, }
cancelCollect (id) { // 取消商品收藏 function cancelCollect (id) { // 取消商品收藏
cancelCollect('GOODS', id).then(res => { apiCancelCollect('GOODS', id).then(res => {
if (res.success) { if (res.success) {
Message.success('取消收藏成功') Message.success('取消收藏成功')
this.getCollectList(); getCollectList();
} }
}) })
}, }
getTracksList () { // 获取足迹列表 function getTracksList () { // 获取足迹列表
const params = { const params = {
pageNumber: 1, pageNumber: 1,
pageSize: 20 pageSize: 20
} }
this.loading = true loading.value = true
tracksList(params).then(res => { fetchTracksList(params).then(res => {
this.tracksList = res.result.records tracksList.value = res.result.records
this.loading = false loading.value = false
}).catch(() => { this.loading = false }) }).catch(() => { loading.value = false })
} }
}
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -20,81 +20,81 @@
</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) // 菜单项
const handleDrawer = ref(false) // 是否可展开
const drawerData = ref('') // 菜单基础数据
// 用户信息
const userInfo = computed(() => {
return storage.getItem('userInfo');
})
// 购物车商品数量
const cartNum = computed(() => appStore.cartNum)
function showCartNum (item) { // 获取购物车数量
if (userInfo.value && item.title === '购物车') {
getCartList()
} }
}, }
components: {drawerPage},
computed: { function clickBar (val) { // tabbar点击操作
// 用户信息 if (!userInfo.value) {
userInfo () {
return Storage.getItem('userInfo');
},
// 购物车商品数量
cartNum () {
return this.$store.state.cartNum
}
},
methods: {
resolveIcon,
showCartNum (item) { // 获取购物车数量
if (this.userInfo && item.title === '购物车') {
this.getCartList()
}
},
clickBar (val) { // tabbar点击操作
if (!this.userInfo) {
Modal.confirm({ Modal.confirm({
title: '请登录', title: '请登录',
content: '请登录后执行此操作', content: '请登录后执行此操作',
okText: '立即登录', okText: '立即登录',
cancelText: '继续浏览', cancelText: '继续浏览',
onOk: () => { onOk: () => {
this.$router.push({ router.push({
path: '/login', path: '/login',
query: { query: {
rePath: this.$route.path, rePath: route.path,
query: JSON.stringify(this.$route.query || {}), query: JSON.stringify(route.query || {}),
}, },
}); });
} }
}); });
} else { } else {
if (val.display) { if (val.display) {
this.handleDrawer = true handleDrawer.value = true
this.drawerData = val drawerData.value = val
} else { } else {
this.handleDrawer = false handleDrawer.value = false
switch (val.title) { switch (val.title) {
case '会员中心': case '会员中心':
this.$router.push('/home') router.push('/home')
break; break;
case '我的资产': case '我的资产':
this.$router.push('/home/MoneyManagement') router.push('/home/MoneyManagement')
break; break;
} }
} }
} }
}, }
getCartList () { // 获取购物车列表
function getCartList () { // 获取购物车列表
cartCount().then(res => { cartCount().then(res => {
this.$store.commit('SET_CARTNUM', res.result) appStore.setCartNum(res.result)
this.Cookies.setItem('cartNum', res.result) storage.setItem('cartNum', res.result)
}) })
}
}
} }
</script> </script>

View File

@@ -11,10 +11,10 @@
</div> </div>
</template> </template>
<script> <script setup>
export default { defineOptions({ name: 'Main' })
name: 'Main',
props: { defineProps({
_Title: { // 描述内容 _Title: { // 描述内容
type: null, type: null,
default: '暂无更多' default: '暂无更多'
@@ -23,8 +23,7 @@ export default {
type: Number, type: Number,
default: 150 default: 150
} }
} })
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -8,15 +8,18 @@
</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", icon:"user",
label:"会员中心", label:"会员中心",
@@ -46,37 +49,34 @@ export default {
label:"顶部", label:"顶部",
path:"back" path:"back"
}, },
], ]);
isScrolling:false, let isScrolling = false;
timeoutId:"", let timeoutId = "";
}
}, onMounted(() => {
mounted() {
const vm = this
// 添加滚轮事件监听器 // 添加滚轮事件监听器
window.addEventListener('wheel', function() { window.addEventListener('wheel', function() {
if (vm.isScrolling) { if (isScrolling) {
// 如果页面正在滚动,则取消定时器并设置 isScrolling 为 false // 如果页面正在滚动,则取消定时器并设置 isScrolling 为 false
window.cancelAnimationFrame(vm.timeoutId); window.cancelAnimationFrame(timeoutId);
vm.isScrolling = false; 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) {
this.timeoutId = window.requestAnimationFrame(this.scrollToTop); timeoutId = window.requestAnimationFrame(scrollToTop);
window.scrollTo(0, c - c / 4); window.scrollTo(0, c - c / 4);
this.isScrolling = true; isScrolling = true;
}else { }else {
this.isScrolling = false; isScrolling = false;
} }
}, }
isLoggedIn () { function isLoggedIn () {
const rawUserInfo = storage.getItem('userInfo'); const rawUserInfo = storage.getItem('userInfo');
if (rawUserInfo) { if (rawUserInfo) {
try { try {
@@ -86,28 +86,26 @@ export default {
} }
} }
return !!storage.getItem('accessToken'); return !!storage.getItem('accessToken');
}, }
goLogin (rePath) { function goLogin (rePath) {
this.$router.push({ router.push({
path: '/login', path: '/login',
query: { query: {
rePath, rePath,
query: JSON.stringify(this.$route.query || {}), query: JSON.stringify(route.query || {}),
}, },
}); });
}, }
handleClickIcon (val) { function handleClickIcon (val) {
if (val.path === 'back') { if (val.path === 'back') {
this.scrollToTop(); scrollToTop();
return; return;
} }
if (val.requireLogin && !this.isLoggedIn()) { if (val.requireLogin && !isLoggedIn()) {
this.goLogin(val.path); goLogin(val.path);
return; return;
} }
this.$router.push(val.path); router.push(val.path);
},
}
} }
</script> </script>

View File

@@ -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: [
// 导航链接 // 导航链接
["购物指南", "购物流程", "会员介绍", "生活旅行", "常见问题"], ["购物指南", "购物流程", "会员介绍", "生活旅行", "常见问题"],
["配送方式", "上门自提", "配送查询", "收取标准", "物流规则"], ["配送方式", "上门自提", "配送查询", "收取标准", "物流规则"],
["支付方式", "在线支付", "公司转账", "余额支付", "积分支付"], ["支付方式", "在线支付", "公司转账", "余额支付", "积分支付"],
["售后服务", "售后政策", "退款说明", "返修/退货", "取消订单"], ["售后服务", "售后政策", "退款说明", "返修/退货", "取消订单"],
], ])
moreLink: ["关于我们", "联系我们", "联系客服", "商家帮助", "隐私政策"], // 更多链接 const moreLink = ref(["关于我们", "联系我们", "联系客服", "商家帮助", "隐私政策"]) // 更多链接
year: new Date().getFullYear(), // 当前年份 const year = ref(new Date().getFullYear()) // 当前年份
};
}, function goArticle() {
methods: {
goArticle() {
// 跳转文章页 // 跳转文章页
this.$router.push("/article"); router.push("/article")
}, }
},
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -1,13 +1,15 @@
<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" });
const props = defineProps({
src: { src: {
type: String, type: String,
default: "", default: "",
@@ -16,63 +18,65 @@ export default {
type: String, type: String,
default: "", default: "",
}, },
}, });
data() {
return { const rootEl = ref(null);
currentSrc: "", const currentSrc = ref("");
loaded: false, const loaded = ref(false);
error: false, const error = ref(false);
observer: null, let observer = null;
};
}, watch(
watch: { () => props.src,
src() { () => {
this.observe(); observe();
},
},
mounted() {
this.observe();
},
beforeUnmount() {
this.disconnect();
},
methods: {
disconnect() {
if (this.observer) {
this.observer.disconnect();
this.observer = null;
} }
}, );
observe() {
this.disconnect(); function disconnect() {
this.currentSrc = ""; if (observer) {
this.loaded = false; observer.disconnect();
this.error = false; observer = null;
if (!this.src) { }
this.error = true; }
function observe() {
disconnect();
currentSrc.value = "";
loaded.value = false;
error.value = false;
if (!props.src) {
error.value = true;
return; return;
} }
if (typeof IntersectionObserver === "undefined") { if (typeof IntersectionObserver === "undefined") {
this.currentSrc = this.src; currentSrc.value = props.src;
return; return;
} }
this.observer = new IntersectionObserver( observer = new IntersectionObserver(
(entries) => { (entries) => {
if (!entries.some((entry) => entry.isIntersecting)) return; if (!entries.some((entry) => entry.isIntersecting)) return;
this.currentSrc = this.src; currentSrc.value = props.src;
this.disconnect(); disconnect();
}, },
{ rootMargin: "160px 0px" } { rootMargin: "160px 0px" }
); );
this.observer.observe(this.$el); observer.observe(rootEl.value);
}, }
onError() {
this.error = true; function onError() {
this.loaded = false; error.value = true;
this.currentSrc = ""; loaded.value = false;
}, currentSrc.value = "";
}, }
};
onMounted(() => {
observe();
});
onBeforeUnmount(() => {
disconnect();
});
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -18,11 +18,11 @@
</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
}, },
@@ -30,21 +30,15 @@ export default {
default: 'goodsDetail', // 设置两个值,goodsDetail和cart,样式不同 default: 'goodsDetail', // 设置两个值,goodsDetail和cart,样式不同
type: String type: String
} }
}, });
data () {
return { const end = ref(''); // 结束状态
end: '', // 结束状态 const hours = ref(''); // 小时
hours: '', // 小时 const minutes = ref(''); // 分钟
minutes: '', // 分钟 const seconds = ref(''); // 秒
seconds: '', // 秒 let interval = null; // 定时器
interval: '' // 定时器
}; function countDown (val) { // 倒计时方法
},
mounted () {
this.init()
},
methods: {
countDown (val) { // 倒计时方法
function addZero (i) { function addZero (i) {
return i < 10 ? '0' + i : i + ''; return i < 10 ? '0' + i : i + '';
} }
@@ -57,21 +51,24 @@ export default {
h = addZero(h); h = addZero(h);
m = addZero(m); m = addZero(m);
s = addZero(s); s = addZero(s);
this.hours = h; hours.value = h;
this.minutes = m minutes.value = m
this.seconds = s; seconds.value = s;
if (lefttime <= 0) { if (lefttime <= 0) {
this.end = `活动已结束`; end.value = `活动已结束`;
clearInterval(this.interval) clearInterval(interval)
} }
}, }
init () { // 初始化
this.interval = setInterval(() => { function init () { // 初始化
this.countDown(this.time); interval = setInterval(() => {
countDown(props.time);
}, 1000); }, 1000);
} }
}
}; onMounted(() => {
init()
});
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.cart-promotion{ .cart-promotion{

View File

@@ -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,154 +347,160 @@ 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: { const emit = defineEmits(["handleClickSku"]);
handler(val) {
this.skuDetail = val.data; const route = useRoute();
this.wholesaleList = val.wholesaleList; const router = useRouter();
this.syncCountWithQuantity();
this.promotion(); const showGoodsVideo = ref(false);
this.swiperGoodsImg(); const goodsVideo = ref("");
}, const wholesaleList = ref([]);
deep: true, const count = ref(1); // 商品数量
immediate: true, const imgIndex = ref(0); // 展示图片下标
}, const currentSelceted = ref([]); // 当前商品sku
showGoodsVideo(val){ const imgList = ref([]); // 商品图片列表
if(val){ const skuDetail = ref({
this.initVideo();
}
}
},
data() {
return {
playIcon,
showGoodsVideo:false,
goodsVideo:"",
wholesaleList: [],
count: 1, // 商品数量
imgIndex: 0, // 展示图片下标
currentSelceted: [], // 当前商品sku
imgList: [], // 商品图片列表
skuDetail: {
specList: [], specList: [],
}, // sku详情 }); // sku详情
goodsSpecList: this.detail.specs, // 商品spec const goodsSpecList = ref(props.detail.specs); // 商品spec
promotionMap: createEmptyPromotionMap(), const promotionMap = ref(createEmptyPromotionMap());
formatList: [], // 选择商品品类的数组 const formatList = ref([]); // 选择商品品类的数组
loading: false, // 立即购买loading const loading = ref(false); // 立即购买loading
loading1: false, // 加入购物车loading const loading1 = ref(false); // 加入购物车loading
loadingPintuan: false, const loadingPintuan = ref(false);
isCollected: false, // 是否收藏 const isCollected = ref(false); // 是否收藏
}; const skuList = ref([]); // formatSku 中赋值(原 data 未声明)
},
components: { Promotion, StarFilled }, // startCheckout 通过字符串 key 操作对应 loading
computed: { const loadings = { loading, loading1, loadingPintuan };
currentImg() {
const item = this.imgList[this.imgIndex]; const currentImg = computed(() => {
const item = imgList.value[imgIndex.value];
return item ? item.url || item : ""; return item ? item.url || item : "";
}, });
wholesalePrice(key) {
return this.wholesaleList.length const wholesalePrice = computed(() => {
? this.wholesaleList.map((item) => { return wholesaleList.value.length
? wholesaleList.value.map((item) => {
return item.price; return item.price;
}) })
: []; : [];
}, });
wholesaleNum(key) {
return this.wholesaleList.length const wholesaleNum = computed(() => {
? this.wholesaleList.map((item) => { return wholesaleList.value.length
? wholesaleList.value.map((item) => {
return item.num; return item.num;
}) })
: []; : [];
}, });
/** 卡密商品:禁加购,仅立即购买;库存读 quantity(M-02 起支持促销) */
isECouponGoods() { /** 卡密商品:禁加购,仅立即购买;库存读 quantity(M-02 起支持促销) */
return isECoupon(this.skuDetail.goodsType); const isECouponGoods = computed(() => {
}, return isECoupon(skuDetail.value.goodsType);
displayStock() { });
if (this.isECouponGoods) {
return getECouponStock(this.skuDetail); const displayStock = computed(() => {
if (isECouponGoods.value) {
return getECouponStock(skuDetail.value);
} }
return Number(this.skuDetail.quantity) || 0; return Number(skuDetail.value.quantity) || 0;
}, });
quantityMax() {
if (this.isECouponGoods) { const quantityMax = computed(() => {
return getECouponMaxBuyNum(this.skuDetail); if (isECouponGoods.value) {
return getECouponMaxBuyNum(skuDetail.value);
} }
const qty = Number(this.skuDetail.quantity) || 0; const qty = Number(skuDetail.value.quantity) || 0;
return qty > 0 ? qty : 1; return qty > 0 ? qty : 1;
}, });
isOutOfStock() {
if (this.isECouponGoods) { const isOutOfStock = computed(() => {
return getECouponStock(this.skuDetail) <= 0; if (isECouponGoods.value) {
return getECouponStock(skuDetail.value) <= 0;
} }
return (Number(this.skuDetail.quantity) || 0) <= 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 }
methods: { );
eCouponAfterSaleHint,
// 初始化video watch(showGoodsVideo, (val) => {
initVideo(){ if (val) {
if(!this.goodsVideo ){ initVideo();
setTimeout(()=>{ }
this.goodsVideo = new DPlayer({ });
// 初始化video
function initVideo() {
if (!goodsVideo.value) {
setTimeout(() => {
goodsVideo.value = new DPlayer({
container: document.getElementById('dplayer'), container: document.getElementById('dplayer'),
video: { video: {
url:this.skuDetail.goodsVideo, url: skuDetail.value.goodsVideo,
}, },
}); });
},100) }, 100);
} }
}
function syncCountWithQuantity() {
const qty = isECouponGoods.value
}, ? getECouponStock(skuDetail.value)
: Number(skuDetail.value.quantity) || 0;
syncCountWithQuantity() {
const qty = this.isECouponGoods
? getECouponStock(this.skuDetail)
: Number(this.skuDetail.quantity) || 0;
if (qty <= 0) { if (qty <= 0) {
this.count = 1; count.value = 1;
return; return;
} }
if (!this.isECouponGoods && this.wholesaleList && this.wholesaleList.length > 0) { if (!isECouponGoods.value && wholesaleList.value && wholesaleList.value.length > 0) {
this.count = Math.min(Math.max(this.wholesaleList[0].num, 1), qty); count.value = Math.min(Math.max(wholesaleList.value[0].num, 1), qty);
return; return;
} }
const max = this.isECouponGoods ? getECouponMaxBuyNum(this.skuDetail) : qty; const max = isECouponGoods.value ? getECouponMaxBuyNum(skuDetail.value) : qty;
if (this.count > max) { if (count.value > max) {
this.count = max; count.value = max;
} else if (this.count < 1) { } else if (count.value < 1) {
this.count = 1; count.value = 1;
} }
}, }
changeCount(val) {
if (this.wholesaleList && this.wholesaleList.length > 0) { function changeCount(val) {
if (this.count <= this.wholesaleList[0].num) { if (wholesaleList.value && wholesaleList.value.length > 0) {
if (count.value <= wholesaleList.value[0].num) {
Message.warning("批发商品购买数量不能小于起批数量"); Message.warning("批发商品购买数量不能小于起批数量");
this.count = this.wholesaleList[0].num; count.value = wholesaleList.value[0].num;
} }
} }
}, }
select(index, value) {
function select(index, value) {
// 选择规格 // 选择规格
this.currentSelceted[index] = value; currentSelceted.value[index] = value;
let selectedSkuId = this.goodsSpecList.find((i) => { let selectedSkuId = goodsSpecList.value.find((i) => {
let matched = true; let matched = true;
let specValues = i.specValues.filter((j) => j.specName !== "images"); let specValues = i.specValues.filter((j) => j.specName !== "images");
for (let n = 0; n < specValues.length; n++) { for (let n = 0; n < specValues.length; n++) {
if (specValues[n].specValue !== this.currentSelceted[n]) { if (specValues[n].specValue !== currentSelceted.value[n]) {
matched = false; matched = false;
return; return;
} }
@@ -500,36 +509,36 @@ export default {
return i; return i;
} }
}); });
this.$emit("handleClickSku", { emit("handleClickSku", {
skuId: selectedSkuId.skuId, skuId: selectedSkuId.skuId,
goodsId: this.skuDetail.goodsId, goodsId: skuDetail.value.goodsId,
}); });
}, }
addShoppingCartBtn() { function addShoppingCartBtn() {
// FR-B-01:E_COUPON 禁止加购物车(后端亦返回 CARD_KEY_E_COUPON_CART_FORBIDDEN) // FR-B-01:E_COUPON 禁止加购物车(后端亦返回 CARD_KEY_E_COUPON_CART_FORBIDDEN)
if (this.isECouponGoods) { if (isECouponGoods.value) {
Message.warning("电子卡券请使用立即购买"); Message.warning("电子卡券请使用立即购买");
return; return;
} }
// 添加购物车 // 添加购物车
const params = { const params = {
num: this.count, num: count.value,
skuId: this.skuDetail.id, skuId: skuDetail.value.id,
}; };
this.loading = true; loading.value = true;
addCartGoods(params) addCartGoods(params)
.then((res) => { .then((res) => {
this.loading = false; loading.value = false;
if (res.success) { if (res.success) {
this.$router.push({ router.push({
path: "/shoppingCart", path: "/shoppingCart",
query: { query: {
skuId: this.skuDetail.id, skuId: skuDetail.value.id,
goodsId: this.skuDetail.goodsId, goodsId: skuDetail.value.goodsId,
thumbnail: this.skuDetail.thumbnail, thumbnail: skuDetail.value.thumbnail,
goodsName: this.skuDetail.goodsName, goodsName: skuDetail.value.goodsName,
count: this.count, count: count.value,
}, },
}); });
} else { } else {
@@ -537,38 +546,42 @@ export default {
} }
}) })
.catch(() => { .catch(() => {
this.loading = false; loading.value = false;
}); });
}, }
buyNow() {
this.startCheckout(E_COUPON_CART_BUY_NOW, "loading1"); function buyNow() {
}, startCheckout(E_COUPON_CART_BUY_NOW, "loading1");
pintuanBuy() { }
this.startCheckout(E_COUPON_CART_PINTUAN, "loadingPintuan");
}, function pintuanBuy() {
pointPay() { startCheckout(E_COUPON_CART_PINTUAN, "loadingPintuan");
this.startCheckout(E_COUPON_CART_POINTS, "loading"); }
},
startCheckout(action, loadingKey) { function pointPay() {
startCheckout(E_COUPON_CART_POINTS, "loading");
}
function startCheckout(action, loadingKey) {
const params = { const params = {
num: this.count, num: count.value,
skuId: this.skuDetail.id, skuId: skuDetail.value.id,
cartType: "BUY_NOW", cartType: "BUY_NOW",
}; };
if (this.skuDetail.goodsType === "VIRTUAL_GOODS") { if (skuDetail.value.goodsType === "VIRTUAL_GOODS") {
params.cartType = "VIRTUAL"; params.cartType = "VIRTUAL";
} else if (this.isECouponGoods) { } else if (isECouponGoods.value) {
params.cartType = resolveECouponCartType({ params.cartType = resolveECouponCartType({
routeWay: this.$route.query.way, routeWay: route.query.way,
action, action,
}); });
} }
this[loadingKey] = true; loadings[loadingKey].value = true;
addCartGoods(params) addCartGoods(params)
.then((res) => { .then((res) => {
this[loadingKey] = false; loadings[loadingKey].value = false;
if (res.success) { if (res.success) {
this.$router.push({ router.push({
path: "/pay", path: "/pay",
query: { way: params.cartType }, query: { way: params.cartType },
}); });
@@ -577,37 +590,39 @@ export default {
} }
}) })
.catch(() => { .catch(() => {
this[loadingKey] = false; loadings[loadingKey].value = false;
}); });
}, }
async collect() {
if (!this.Cookies.getItem("accessToken") || !this.Cookies.getItem("userInfo")) { async function collect() {
this.$router.push({ if (!storage.getItem("accessToken") || !storage.getItem("userInfo")) {
router.push({
path: "/login", path: "/login",
query: { query: {
rePath: this.$route.path, rePath: route.path,
query: JSON.stringify(this.$route.query || {}), query: JSON.stringify(route.query || {}),
}, },
}); });
return; return;
} }
// 收藏商品 // 收藏商品
if (this.isCollected) { if (isCollected.value) {
let cancel = await cancelCollect("GOODS", this.skuDetail.id); let cancel = await cancelCollect("GOODS", skuDetail.value.id);
if (cancel.success) { if (cancel.success) {
Message.success("取消收藏成功"); Message.success("取消收藏成功");
this.isCollected = false; isCollected.value = false;
} }
} else { } else {
let collect = await collectGoods("GOODS", this.skuDetail.id); let collect = await collectGoods("GOODS", skuDetail.value.id);
if (collect.code === 200) { if (collect.code === 200) {
this.isCollected = true; isCollected.value = true;
Message.success("收藏商品成功,可以前往个人中心我的收藏查看"); Message.success("收藏商品成功,可以前往个人中心我的收藏查看");
} }
} }
}, }
// 格式化数据
formatSku(list) { // 格式化数据
function formatSku(list) {
let arr = [{}]; let arr = [{}];
list.forEach((item, index) => { list.forEach((item, index) => {
item.specValues.forEach((spec, specIndex) => { item.specValues.forEach((spec, specIndex) => {
@@ -642,19 +657,20 @@ export default {
}); });
}); });
arr.shift(); arr.shift();
this.formatList = arr; formatList.value = arr;
let cur = list.filter((i) => i.skuId === this.$route.query.skuId)[0]; let cur = list.filter((i) => i.skuId === route.query.skuId)[0];
if (cur) { if (cur) {
cur.specValues cur.specValues
.filter((i) => i.specName !== "images") .filter((i) => i.specName !== "images")
.forEach((value, _index) => { .forEach((value, _index) => {
this.currentSelceted[_index] = value.specValue; currentSelceted.value[_index] = value.specValue;
}); });
} }
this.skuList = list; skuList.value = list;
}, }
receiveCoupon(id) {
function handleReceiveCoupon(id) {
// 领取优惠券 // 领取优惠券
receiveCoupon(id).then((res) => { receiveCoupon(id).then((res) => {
if (res.success) { if (res.success) {
@@ -663,56 +679,57 @@ export default {
Message.warning(res.message); Message.warning(res.message);
} }
}); });
}, }
promotion() {
this.promotionMap = createEmptyPromotionMap(); function promotion() {
if (!this.detail || !this.detail.promotionMap) return false; promotionMap.value = createEmptyPromotionMap();
const keysArr = Object.keys(this.detail.promotionMap); if (!props.detail || !props.detail.promotionMap) return false;
const keysArr = Object.keys(props.detail.promotionMap);
if (keysArr.length === 0) return false; if (keysArr.length === 0) return false;
for (let i = 0; i < keysArr.length; i++) { for (let i = 0; i < keysArr.length; i++) {
const key = keysArr[i].split("-")[0]; const key = keysArr[i].split("-")[0];
if (key === "COUPON") { if (key === "COUPON") {
if (!Array.isArray(this.promotionMap.COUPON)) { if (!Array.isArray(promotionMap.value.COUPON)) {
this.promotionMap.COUPON = []; promotionMap.value.COUPON = [];
} }
this.promotionMap.COUPON.push(this.detail.promotionMap[keysArr[i]]); promotionMap.value.COUPON.push(props.detail.promotionMap[keysArr[i]]);
} else if (Object.prototype.hasOwnProperty.call(this.promotionMap, key)) { } else if (Object.prototype.hasOwnProperty.call(promotionMap.value, key)) {
this.promotionMap[key] = this.detail.promotionMap[keysArr[i]]; promotionMap.value[key] = props.detail.promotionMap[keysArr[i]];
} else { } else {
this.promotionMap[key] = this.detail.promotionMap[keysArr[i]]; promotionMap.value[key] = props.detail.promotionMap[keysArr[i]];
} }
} }
}, }
// 相册里会混进规格 JSON,只留图片地址;没有相册时用主图
swiperGoodsImg() { // 相册里会混进规格 JSON,只留图片地址;没有相册时用主图
const source = this.skuDetail.goodsGalleryList || []; function swiperGoodsImg() {
const source = skuDetail.value.goodsGalleryList || [];
const gallery = Array.from(source).filter((item) => { const gallery = Array.from(source).filter((item) => {
if (typeof item !== "string") return false; if (typeof item !== "string") return false;
return item.indexOf('"url":') === -1 && item.indexOf('"status":') === -1; return item.indexOf('"url":') === -1 && item.indexOf('"status":') === -1;
}); });
if (gallery.length) { if (gallery.length) {
this.imgList = gallery; imgList.value = gallery;
return; return;
} }
this.imgList = this.skuDetail.original ? [this.skuDetail.original] : []; imgList.value = skuDetail.value.original ? [skuDetail.value.original] : [];
}, }
},
mounted() { onMounted(() => {
// 用户登录才会判断是否收藏(须同时有 token,避免残留 userInfo 触发 401 跳转登录) // 用户登录才会判断是否收藏(须同时有 token,避免残留 userInfo 触发 401 跳转登录)
if (this.Cookies.getItem("accessToken") && this.Cookies.getItem("userInfo")) { if (storage.getItem("accessToken") && storage.getItem("userInfo")) {
isCollection("GOODS", this.skuDetail.id).then((res) => { isCollection("GOODS", skuDetail.value.id).then((res) => {
if (res.success && res.result) { if (res.success && res.result) {
this.isCollected = true; isCollected.value = true;
} }
}); });
} }
this.formatSku(this.goodsSpecList); formatSku(goodsSpecList.value);
// this.promotion(); // promotion();
document.title = this.skuDetail.goodsName; document.title = skuDetail.value.goodsName;
}, });
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -132,48 +132,51 @@
</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', const props = defineProps({
props: {
detail: { detail: {
type: Object, type: Object,
default: null default: null
} }
}, });
data () {
return { const tabNav = ref(null);
detailTabs: [ const intro = ref(null);
const comment = ref(null);
const params = ref(null);
const sectionRefs = { intro, comment, params };
const detailTabs = ref([
{ name: 'intro', label: '商品介绍' }, { name: 'intro', label: '商品介绍' },
{ name: 'comment', label: '商品评价' }, { name: 'comment', label: '商品评价' },
{ name: 'params', label: '商品参数' } { name: 'params', label: '商品参数' }
], ]);
activeDetailTab: 'intro', const activeDetailTab = ref('intro');
anchorStuck: false, const anchorStuck = ref(false);
lockScrollSpy: false, const lockScrollSpy = ref(false);
scrollTimer: null, let scrollTimer = null;
commentList: [], const commentList = ref([]);
commentParams: { const commentParams = reactive({
pageNumber: 1, pageNumber: 1,
pageSize: 10, pageSize: 10,
grade: '', grade: '',
goodsId: '' goodsId: ''
}, });
commentTypeNum: {}, const commentTypeNum = ref({});
commentTotal: 0 const commentTotal = ref(0);
};
}, const skuDetail = computed(() => {
computed: { return props.detail.data;
skuDetail () { });
return this.detail.data; const goodsParamsList = computed(() => {
}, const list = props.detail && Array.isArray(props.detail.goodsParamsDTOList) ? props.detail.goodsParamsDTOList : [];
goodsParamsList () {
const list = this.detail && Array.isArray(this.detail.goodsParamsDTOList) ? this.detail.goodsParamsDTOList : [];
const flat = []; const flat = [];
list.forEach((item) => { list.forEach((item) => {
@@ -194,98 +197,97 @@ export default {
const sb = Number(b && b.sort) || 0; const sb = Number(b && b.sort) || 0;
return sa - sb; return sa - sb;
}); });
} });
},
methods: { function getNavOffset () {
getNavOffset () { const nav = tabNav.value;
const nav = this.$refs.tabNav;
return (nav ? nav.offsetHeight : 52) + 8; return (nav ? nav.offsetHeight : 52) + 8;
}, }
scrollToSection (name) { function scrollToSection (name) {
const el = this.$refs[name]; const el = sectionRefs[name] && sectionRefs[name].value;
if (!el) return; if (!el) return;
this.activeDetailTab = name; activeDetailTab.value = name;
this.lockScrollSpy = true; lockScrollSpy.value = true;
const top = el.getBoundingClientRect().top + window.pageYOffset - this.getNavOffset(); const top = el.getBoundingClientRect().top + window.pageYOffset - getNavOffset();
window.scrollTo({ top: Math.max(0, top), behavior: 'smooth' }); window.scrollTo({ top: Math.max(0, top), behavior: 'smooth' });
const unlock = () => { const unlock = () => {
this.lockScrollSpy = false; lockScrollSpy.value = false;
window.removeEventListener('scrollend', unlock); window.removeEventListener('scrollend', unlock);
}; };
window.addEventListener('scrollend', unlock); window.addEventListener('scrollend', unlock);
window.clearTimeout(this.scrollTimer); window.clearTimeout(scrollTimer);
this.scrollTimer = window.setTimeout(unlock, 900); scrollTimer = window.setTimeout(unlock, 900);
}, }
handleScroll () { function handleScroll () {
const nav = this.$refs.tabNav; const nav = tabNav.value;
if (nav) { if (nav) {
this.anchorStuck = nav.getBoundingClientRect().top <= 1; anchorStuck.value = nav.getBoundingClientRect().top <= 1;
} }
if (this.lockScrollSpy) return; if (lockScrollSpy.value) return;
const offset = this.getNavOffset() + 16; const offset = getNavOffset() + 16;
let current = ''; let current = '';
this.detailTabs.forEach((tab) => { detailTabs.value.forEach((tab) => {
const el = this.$refs[tab.name]; const el = sectionRefs[tab.name] && sectionRefs[tab.name].value;
if (el && el.getBoundingClientRect().top <= offset) { if (el && el.getBoundingClientRect().top <= offset) {
current = tab.name; current = tab.name;
} }
}); });
if (current) { if (current) {
this.activeDetailTab = current; activeDetailTab.value = current;
} }
}, }
changePageNum (val) { function changePageNum (val) {
this.commentParams.pageNumber = val; commentParams.pageNumber = val;
this.getList(); getList();
}, }
changePageSize (val) { function changePageSize (val) {
this.commentParams.pageNumber = 1; commentParams.pageNumber = 1;
this.commentParams.pageSize = val; commentParams.pageSize = val;
this.getList(); getList();
}, }
getList () { function getList () {
this.commentParams.goodsId = this.skuDetail.goodsId; commentParams.goodsId = skuDetail.value.goodsId;
goodsComment(this.commentParams).then(res => { goodsComment(commentParams).then(res => {
if (res.success) { if (res.success) {
this.commentList = res.result.records; commentList.value = res.result.records;
this.commentTotal = res.result.total; commentTotal.value = res.result.total;
} }
}); });
goodsCommentNum(this.skuDetail.goodsId).then(res => { goodsCommentNum(skuDetail.value.goodsId).then(res => {
if (res.success) { if (res.success) {
this.commentTypeNum = res.result; commentTypeNum.value = res.result;
} }
}); });
}, }
viewByGrade (grade) { function viewByGrade (grade) {
this.commentParams.grade = grade; commentParams.grade = grade;
this.commentParams.pageNumber = 1; commentParams.pageNumber = 1;
this.getList(); getList();
}, }
previewImg (img, item) { function previewImg (img, item) {
item.previewImg = img; item.previewImg = img;
item.deg = item.deg || 0; item.deg = item.deg || 0;
}, }
hidePreviewImg (item) { function hidePreviewImg (item) {
item.previewImg = ''; item.previewImg = '';
}, }
rotatePreviewImg (type, item) { function rotatePreviewImg (type, item) {
const current = item.deg || 0; const current = item.deg || 0;
item.deg = type ? current + 90 : current - 90; 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;
} }
}, });
mounted () {
window.addEventListener('scroll', this.handleScroll, { passive: true }); onBeforeUnmount(() => {
this.getList(); window.removeEventListener('scroll', handleScroll);
if (this.skuDetail.grade === null || this.skuDetail.grade === undefined) { window.clearTimeout(scrollTimer);
this.skuDetail.grade = 100; });
}
},
beforeUnmount () {
window.removeEventListener('scroll', this.handleScroll);
window.clearTimeout(this.scrollTimer);
}
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -65,17 +65,20 @@
</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: "",
@@ -92,56 +95,59 @@ export default {
type: String, type: String,
default: "", default: "",
}, },
}, });
data() {
return { const route = useRoute();
store: {}, const router = useRouter();
logoError: false,
collected: false, const store = ref({});
recommends: [], const logoError = ref(false);
}; const collected = ref(false);
}, const recommends = ref([]);
computed: { // 原 talkIm mixin 的 IM 链接
storeName() { const IMLink = ref("");
return this.store.storeName || this.storeNameFallback || "店铺";
}, const storeName = computed(() => {
storeInitial() { return store.value.storeName || props.storeNameFallback || "店铺";
return this.storeName.slice(0, 1); });
}, const storeInitial = computed(() => {
// 自营标记后端可能是 true、1 或 "1" return storeName.value.slice(0, 1);
isSelfOperated() { });
const flag = this.store.selfOperated; // 自营标记后端可能是 true、1 或 "1"
const isSelfOperated = computed(() => {
const flag = store.value.selfOperated;
return flag === true || flag === 1 || flag === "1"; return flag === true || flag === 1 || flag === "1";
}, });
},
watch: { watch(
storeId: { () => props.storeId,
immediate: true, (id) => {
handler(id) {
if (!id) return; if (!id) return;
this.logoError = false; logoError.value = false;
this.loadStore(id); loadStore(id);
this.loadRecommend(id); loadRecommend(id);
this.loadCollected(id); loadCollected(id);
}, },
}, { immediate: true }
}, );
methods: {
// 没有评分或小于等于 0 时按满分展示 // 没有评分或小于等于 0 时按满分展示
formatScore(value) { function formatScore(value) {
const score = Number(value); const score = Number(value);
if (!Number.isFinite(score) || score <= 0) return "5.00"; if (!Number.isFinite(score) || score <= 0) return "5.00";
return score.toFixed(2); return score.toFixed(2);
}, }
loadStore(id) {
function loadStore(id) {
getStoreById(id).then((res) => { getStoreById(id).then((res) => {
if (res.success && res.result) this.store = res.result; if (res.success && res.result) store.value = res.result;
}).catch((err) => { }).catch((err) => {
console.warn('获取店铺信息失败', err); console.warn('获取店铺信息失败', err);
}); });
}, }
// 按销量取店内商品,去掉当前商品,最多 4 个
loadRecommend(id) { // 按销量取店内商品,去掉当前商品,最多 4 个
function loadRecommend(id) {
goodsList({ goodsList({
storeId: id, storeId: id,
pageNumber: 1, pageNumber: 1,
@@ -150,64 +156,126 @@ export default {
order: "desc", order: "desc",
}).then((res) => { }).then((res) => {
const records = (res.success && res.result && res.result.records) || []; const records = (res.success && res.result && res.result.records) || [];
this.recommends = records recommends.value = records
.filter((item) => String(item.goodsId) !== String(this.goodsId)) .filter((item) => String(item.goodsId) !== String(props.goodsId))
.slice(0, 4); .slice(0, 4);
}).catch(() => { }).catch(() => {
// 推荐商品为可选数据,加载失败时静默 // 推荐商品为可选数据,加载失败时静默
}); });
}, }
// 同时有 token 和用户信息才查收藏,避免残留 userInfo 触发登录跳转
loadCollected(id) { // 同时有 token 和用户信息才查收藏,避免残留 userInfo 触发登录跳转
if (!this.Cookies.getItem("accessToken") || !this.Cookies.getItem("userInfo")) return; function loadCollected(id) {
if (!storage.getItem("accessToken") || !storage.getItem("userInfo")) return;
isStoreCollection("STORE", id).then((res) => { isStoreCollection("STORE", id).then((res) => {
if (res.success && res.result) this.collected = true; if (res.success && res.result) collected.value = true;
}).catch(() => { }).catch(() => {
// 收藏状态为可选数据,失败时静默 // 收藏状态为可选数据,失败时静默
}); });
}, }
goStore() {
this.$router.push({ path: "/merchant", query: { id: this.storeId } }); function goStore() {
}, router.push({ path: "/merchant", query: { id: props.storeId } });
// 推荐商品新开详情页,店铺页仍在当前页打开 }
goGoods(item) {
const href = this.$router.resolve({ // 推荐商品新开详情页,店铺页仍在当前页打开
function goGoods(item) {
const href = router.resolve({
path: "/goodsDetail", path: "/goodsDetail",
query: { skuId: item.id, goodsId: item.goodsId }, query: { skuId: item.id, goodsId: item.goodsId },
}).href; }).href;
window.open(href, "_blank"); window.open(href, "_blank");
}, }
contact() {
this.IMService(this.storeId, this.goodsId, this.skuId); // 原 talkIm mixin 的登录提示
}, function promptLogin() {
// 未登录先去登录,并带回当前商品页 Modal.confirm({
async toggleCollect() { title: "温馨提示",
if (!this.Cookies.getItem("accessToken") || !this.Cookies.getItem("userInfo")) { content: "请登录后执行此操作",
this.$router.push({ okText: "立即登录",
cancelText: "取消",
onOk: () => {
router.push({
path: "/login", path: "/login",
query: { query: {
rePath: this.$route.path, rePath: route.path,
query: JSON.stringify(this.$route.query || {}), 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; return;
} }
if (this.collected) { if (collected.value) {
const cancel = await cancelStoreCollect("STORE", this.storeId); const cancel = await cancelStoreCollect("STORE", props.storeId);
if (cancel.success) { if (cancel.success) {
this.collected = false; collected.value = false;
Message.success("已取消收藏"); Message.success("已取消收藏");
} }
return; return;
} }
const collect = await collectStore("STORE", this.storeId); const collect = await collectStore("STORE", props.storeId);
if (collect.code === 200) { if (collect.code === 200) {
this.collected = true; collected.value = true;
Message.success("收藏店铺成功,可以前往个人中心我的收藏查看"); Message.success("收藏店铺成功,可以前往个人中心我的收藏查看");
} }
}, }
},
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -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,79 +100,74 @@ 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', const route = useRoute();
created() { const router = useRouter();
if (storage.getItem('userInfo')) { const appStore = useAppStore();
this.userInfo = JSON.parse(storage.getItem('userInfo'));
const userInfo = ref({}); // 用户信息
const shoppingCart = ref([]); // 购物车
const cartLoading = ref(false);
const cartLoadedAt = ref(0);
const cartCacheTime = ref(5000);
// 购物车商品数量
const cartNum = computed(() => appStore.cartNum);
const unreadMsg = computed(() => appStore.unreadMsg);
const unreadLabel = computed(() => {
return unreadMsg.value > 99 ? '99+' : String(unreadMsg.value);
});
if (storage.getItem('userInfo')) {
userInfo.value = JSON.parse(storage.getItem('userInfo'));
refreshUnreadMessage(); refreshUnreadMessage();
} }
},
data() { function goToPay() {
return { if (!userInfo.value.username) {
config,
userInfo: {}, // 用户信息
shoppingCart: [], // 购物车
cartLoading: false,
cartLoadedAt: 0,
cartCacheTime: 5000
};
},
computed: {
// 购物车商品数量
cartNum() {
return this.$store.state.cartNum;
},
unreadMsg() {
return Number(this.$store.state.unreadMsg) || 0;
},
unreadLabel() {
return this.unreadMsg > 99 ? '99+' : String(this.unreadMsg);
}
},
methods: {
goToPay() {
if (!this.userInfo.username) {
Modal.confirm({ Modal.confirm({
title: '温馨提示', title: '温馨提示',
content: '请登录后执行此操作', content: '请登录后执行此操作',
okText: '立即登录', okText: '立即登录',
cancelText: '取消', cancelText: '取消',
onOk: () => { onOk: () => {
this.$router.push({ router.push({
path: '/login', path: '/login',
query: { query: {
rePath: '/cart', rePath: '/cart',
query: JSON.stringify(this.$route.query || {}), query: JSON.stringify(route.query || {}),
}, },
}); });
}, },
}); });
return; return;
} }
this.$router.push('/cart'); router.push('/cart');
}, }
myInfo() { // 跳转会员中心
this.$router.push('/home'); function myInfo() { // 跳转会员中心
}, router.push('/home');
signOutFun() { // 退出登录 }
function signOutFun() { // 退出登录
logout().then(res => { logout().then(res => {
storage.removeItem('accessToken'); storage.removeItem('accessToken');
storage.removeItem('refreshToken'); storage.removeItem('refreshToken');
storage.removeItem('userInfo'); storage.removeItem('userInfo');
storage.removeItem('cartNum'); storage.removeItem('cartNum');
this.$store.commit('SET_CARTNUM', 0) appStore.setCartNum(0)
this.$store.commit('SET_UNREAD_MSG', 0) appStore.setUnreadMsg(0)
this.$router.push('/login'); router.push('/login');
}); });
}, }
goUserCenter(path) {
function goUserCenter(path) {
// 跳转我的订单,我的足迹、收藏等 // 跳转我的订单,我的足迹、收藏等
if (this.userInfo.username) { if (userInfo.value.username) {
this.$router.push({path: path}); router.push({path: path});
} else { } else {
Modal.confirm({ Modal.confirm({
title: '请登录', title: '请登录',
@@ -178,20 +175,21 @@ export default {
okText: '立即登录', okText: '立即登录',
cancelText: '继续浏览', cancelText: '继续浏览',
onOk: () => { onOk: () => {
this.$router.push({ router.push({
path: '/login', path: '/login',
query: { query: {
rePath: this.$route.path, rePath: route.path,
query: JSON.stringify(this.$route.query || {}), query: JSON.stringify(route.query || {}),
}, },
}); });
} }
}); });
} }
}, }
shopEntry() {
function shopEntry() {
if (storage.getItem('accessToken')) { if (storage.getItem('accessToken')) {
this.$router.push({ router.push({
path: '/shopEntry', path: '/shopEntry',
query: { id: 1 }, query: { id: 1 },
}); });
@@ -203,7 +201,7 @@ export default {
okText: '立即登录', okText: '立即登录',
cancelText: '取消', cancelText: '取消',
onOk: () => { onOk: () => {
this.$router.push({ router.push({
path: '/login', path: '/login',
query: { query: {
rePath: '/shopEntry', rePath: '/shopEntry',
@@ -212,35 +210,35 @@ export default {
}); });
}, },
}); });
}, }
handleCartDropdownVisible(visible) {
function handleCartDropdownVisible(visible) {
if (visible) { if (visible) {
this.getCartList(); getCartList();
} }
}, }
getCartList() {
function getCartList() {
// 获取购物车列表 // 获取购物车列表
if (!this.userInfo.username || this.cartLoading) { if (!userInfo.value.username || cartLoading.value) {
return; return;
} }
const now = Date.now(); const now = Date.now();
if (this.cartLoadedAt && now - this.cartLoadedAt < this.cartCacheTime) { if (cartLoadedAt.value && now - cartLoadedAt.value < cartCacheTime.value) {
return; return;
} }
this.cartLoadedAt = now; cartLoadedAt.value = now;
this.cartLoading = true; cartLoading.value = true;
cartGoodsAll().then((res) => { cartGoodsAll().then((res) => {
if (res.success) { if (res.success) {
this.shoppingCart = (res.result && res.result.skuList) || []; shoppingCart.value = (res.result && res.result.skuList) || [];
this.$store.commit('SET_CARTNUM', this.shoppingCart.length); appStore.setCartNum(shoppingCart.value.length);
this.Cookies.setItem('cartNum', this.shoppingCart.length); storage.setItem('cartNum', shoppingCart.value.length);
} }
}).finally(() => { }).finally(() => {
this.cartLoading = false; cartLoading.value = false;
}); });
} }
}
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -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() {
},
methods: {
goCartList() {
this.$router.push('/cart')
},
getCartList() {
if (storage.getItem('userInfo')) { if (storage.getItem('userInfo')) {
cartCount().then((res) => { cartCount().then((res) => {
this.$store.commit('SET_CARTNUM', res.result) appStore.setCartNum(res.result)
this.Cookies.setItem('cartNum', res.result) storage.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;

View File

@@ -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() {
const list = this.data?.list || [];
return list.filter( return list.filter(
(el) => el && el.key && !this.isBuiltinModule(el.type) (el) => el && el.key && !isBuiltinModule(el.type)
); );
} });
},
methods: { function isBuiltinModule(type) {
isBuiltinModule(type) {
// 顶部广告、快捷导航由 Index 页单独渲染 // 顶部广告、快捷导航由 Index 页单独渲染
return type === 'topAdvert' || type === 'navBar'; return type === 'topAdvert' || type === 'navBar';
} }
}
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.model-form { .model-form {

View File

@@ -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 {

View File

@@ -82,30 +82,31 @@
</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 || [];
}, });
},
data() { const displaySlides = computed(() => {
return { return carouselList.value.filter((item) => item && item.img);
config, });
defaultAvatar,
userInfo: {}, // 用户信息 const userInfo = ref({}); // 用户信息
entranceList: [ const entranceList = ref([
{ {
icon: "collage", icon: "collage",
label: "宝贝收藏", label: "宝贝收藏",
@@ -126,8 +127,8 @@ export default {
label: "我的足迹", label: "我的足迹",
path: "/home/MyTracks" path: "/home/MyTracks"
}, },
], ]);
appendList: [ const appendList = ref([
{ {
icon: "support", icon: "support",
label: "帮助中心", label: "帮助中心",
@@ -148,8 +149,8 @@ export default {
label: "我的消息", label: "我的消息",
path: "/home/MsgList" path: "/home/MsgList"
}, },
], ]);
recentList: [ const recentList = ref([
{ {
value: "0", value: "0",
label: "购物车" label: "购物车"
@@ -166,15 +167,13 @@ export default {
value: "0", value: "0",
label: "待付款" label: "待付款"
}, },
], ]);
};
}, function carouselItemKey(item, index) {
methods: {
getIconUrl,
carouselItemKey(item, index) {
return item?.img || item?.url || `slide-${index}`; return item?.img || item?.url || `slide-${index}`;
}, }
resolveRoute(pathStr) {
function resolveRoute(pathStr) {
if (!pathStr) return { path: "/" }; if (!pathStr) return { path: "/" };
const [path, search] = pathStr.split("?"); const [path, search] = pathStr.split("?");
if (!search) return { path }; if (!search) return { path };
@@ -182,23 +181,26 @@ export default {
path, path,
query: Object.fromEntries(new URLSearchParams(search)), query: Object.fromEntries(new URLSearchParams(search)),
}; };
}, }
isMemberRoute(location) {
function isMemberRoute(location) {
const path = location.path && location.path.charAt(0) === "/" ? location.path : `/${location.path || ""}`; const path = location.path && location.path.charAt(0) === "/" ? location.path : `/${location.path || ""}`;
return path === "/home" || path.startsWith("/home/") || path === "/cart"; return path === "/home" || path.startsWith("/home/") || path === "/cart";
}, }
normalizeLocation(location) {
function normalizeLocation(location) {
if (!location.path || location.path.charAt(0) === "/") return location; if (!location.path || location.path.charAt(0) === "/") return location;
return { ...location, path: `/${location.path}` }; return { ...location, path: `/${location.path}` };
}, }
promptLoginThenNavigate(location) {
function promptLoginThenNavigate(location) {
Modal.confirm({ Modal.confirm({
title: "温馨提示", title: "温馨提示",
content: "请登录后执行此操作", content: "请登录后执行此操作",
okText: "立即登录", okText: "立即登录",
cancelText: "取消", cancelText: "取消",
onOk: () => { onOk: () => {
this.$router.push({ router.push({
path: "/login", path: "/login",
query: { query: {
rePath: location.path, rePath: location.path,
@@ -207,26 +209,26 @@ export default {
}); });
}, },
}); });
}, }
goLogin() {
this.$router.push("/login"); function goLogin() {
}, router.push("/login");
// 快捷跳转中心 }
entryControl(val) {
const location = this.normalizeLocation(this.resolveRoute(val.path)); // 快捷跳转中心
if (this.isMemberRoute(location) && !storage.getItem("userInfo")) { function entryControl(val) {
this.promptLoginThenNavigate(location); const location = normalizeLocation(resolveRoute(val.path));
if (isMemberRoute(location) && !storage.getItem("userInfo")) {
promptLoginThenNavigate(location);
return; return;
} }
this.$router.push(location); router.push(location);
}, }
},
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 scoped lang="scss"> <style scoped lang="scss">

View File

@@ -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">

View File

@@ -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 () { // 获取常见问题列表
articleList(this.params).then(res => {
if (res.success) { if (res.success) {
this.articleList = res.result.records articleList.value = res.result.records
} }
}) })
}, }
goArticle (id) { // 跳转文章详情
this.$router.push({ function goArticle (id) { // 跳转文章详情
router.push({
path: '/article', path: '/article',
query: {id} query: {id}
}); });
} }
},
mounted () { onMounted(() => {
if (storage.getItem('userInfo')) this.userInfo = JSON.parse(storage.getItem('userInfo')); if (storage.getItem('userInfo')) userInfo.value = JSON.parse(storage.getItem('userInfo'));
this.getArticleList() getArticleList()
} })
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -19,20 +19,17 @@
</div> </div>
</div> </div>
</template> </template>
<script> <script setup>
export default { import { ref } from 'vue';
props: {
const props = defineProps({
data: { data: {
type: Object, type: Object,
default: null default: null
} }
}, });
data () {
return { const options = ref(props.data.options); // 装修数据
options: this.data.options // 装修数据
};
}
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.first-page-advert { .first-page-advert {

View File

@@ -70,21 +70,17 @@
</div> </div>
</div> </div>
</template> </template>
<script> <script setup>
export default { import { ref } from 'vue'
props: {
const props = defineProps({
data: { data: {
type: Object, type: Object,
default: null default: null
} }
}, })
data () {
return { const options = ref(props.data.options) // 装修数据
options: this.data.options // 装修数据
};
},
methods: {}
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.new-goods { .new-goods {

View File

@@ -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({
props: {
data: { data: {
type: Object, type: Object,
default: null, default: null,
}, },
}, });
data() {
return { const emit = defineEmits(['content']);
screenHeight:document.body.clientHeight,
scrollHieght:0, const router = useRouter();
topSearchShow: true, //展示 导航条
topIndex: 0, // 当前滚动条位置 (取整) const obtain = ref(null);
scrollTops: 0, const screenHeight = ref(document.body.clientHeight);
open:'', const scrollHieght = ref(0);
currentIndex: 0, // 当前分类下标 const topSearchShow = ref(true); //展示 导航条
conData: this.data, // 装修数据 const topIndex = ref(0); // 当前滚动条位置 (取整)
showContent: true, // 是否展示内容 const scrollTops = ref(0);
}; const open = ref('');
}, const currentIndex = ref(0); // 当前分类下标
watch: { const conData = ref(props.data); // 装修数据
data: function (val) { const showContent = ref(true); // 是否展示内容
this.conData = val;
}, watch(
conData: function (val) { () => props.data,
this.$emit("content", val); (val) => {
}, conData.value = val;
}, }
methods: { );
handleScrollx(){
watch(conData, (val) => {
emit('content', val);
});
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 path = path;
var Id = id; var Id = id;
localStorage.setItem('toId',Id); localStorage.setItem('toId', Id);
this.$router.push(path); router.push(path);
}, }
change(status){ //获取是否获取到图钉
this.open = status function change(status) { //获取是否获取到图钉
}, open.value = status
gotoDemo(){ // 跳转到demo的位置 }
if(this.open){ // 获取到图钉之后在跳转当前位置
function gotoDemo() { // 跳转到demo的位置
if (open.value) { // 获取到图钉之后在跳转当前位置
document.querySelector("#demo").scrollIntoView(true); document.querySelector("#demo").scrollIntoView(true);
} }
//scrollIntoView()可以在所有的HTML元素上调用,通过滚动浏览器窗口或某个容器元素 //scrollIntoView()可以在所有的HTML元素上调用,通过滚动浏览器窗口或某个容器元素
}, }
changeCurr(index) {
function changeCurr(index) {
// 选择分类 // 选择分类
this.currentIndex = index; currentIndex.value = index;
}, }
},
}; onMounted(() => {
window.addEventListener('scroll', handleScrollx, true)
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.not-enough { .not-enough {

View File

@@ -58,22 +58,18 @@
</div> </div>
</div> </div>
</template> </template>
<script> <script setup>
export default { import { ref } from 'vue';
props: {
const props = defineProps({
data: { data: {
type: Object, type: Object,
default: () => ({}) default: () => ({})
} }
}, });
data () {
return { const msgLeft = ref(props.data.options.contentLeft); // 左侧数据
msgLeft: this.data.options.contentLeft, // 左侧数据 const msgRight = ref(props.data.options.contentRight); // 右侧数据
msgRight: this.data.options.contentRight // 右侧数据
};
},
methods: {}
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.recommend { .recommend {

View File

@@ -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";
const props = defineProps({
data: Object, data: Object,
}, });
data() {
return { const router = useRouter();
list: [],
goodsList: [], const list = ref([]);
actStatus: 0, const goodsList = ref([]);
actName: "限时秒杀", const actStatus = ref(0);
currIndex: 0, const actName = ref("限时秒杀");
currHour: "00", const currIndex = ref(0);
diffSeconds: 0, const currHour = ref("00");
hours: 0, const diffSeconds = ref(0);
minutes: 0, const hours = ref(0);
seconds: 0, const minutes = ref(0);
interval: null, const seconds = ref(0);
}; const interval = ref(null);
},
watch: { watch(currIndex, (val) => {
currIndex(val) { clearInterval(interval.value);
clearInterval(this.interval); interval.value = null;
this.interval = null; countDown(val);
this.countDown(val); goodsList.value = list.value[val]?.seckillGoodsList || [];
this.goodsList = this.list[val]?.seckillGoodsList || []; });
},
diffSeconds(val) { watch(diffSeconds, (val) => {
const hours = Math.floor(val / 3600); const h = Math.floor(val / 3600);
const minutes = Math.floor(val / 60) % 60; const m = Math.floor(val / 60) % 60;
const seconds = val % 60; const s = val % 60;
this.hours = filteTime(hours); hours.value = filteTime(h);
this.minutes = filteTime(minutes); minutes.value = filteTime(m);
this.seconds = filteTime(seconds); seconds.value = filteTime(s);
if (val <= 0) { if (val <= 0) {
clearInterval(this.interval); clearInterval(interval.value);
this.interval = null; interval.value = null;
} }
function filteTime(time) { function filteTime(time) {
return time < 10 ? "0" + time : time; return time < 10 ? "0" + time : time;
} }
}, });
},
beforeUnmount() { onBeforeUnmount(() => {
clearInterval(this.interval); clearInterval(interval.value);
}, });
mounted() {
this.getListByDay(); onMounted(() => {
}, getListByDay();
methods: { });
goToSeckill() {
this.$router.push("/seckill"); function goToSeckill() {
}, router.push("/seckill");
countDown(currIndex) { }
function countDown(index) {
const zeroTime = new Date(new Date().toLocaleDateString()).getTime(); const zeroTime = new Date(new Date().toLocaleDateString()).getTime();
const currTime = new Date().getTime(); const currTime = new Date().getTime();
let actTime = 0; let actTime = 0;
const nowHour = new Date().getHours(); const nowHour = new Date().getHours();
if (this.list[currIndex].timeLine > nowHour) { if (list.value[index].timeLine > nowHour) {
this.actStatus = 0; actStatus.value = 0;
actTime = zeroTime + this.list[currIndex].timeLine * 3600 * 1000; actTime = zeroTime + list.value[index].timeLine * 3600 * 1000;
} else if (this.list[currIndex].timeLine <= nowHour) { } else if (list.value[index].timeLine <= nowHour) {
this.actStatus = 1; actStatus.value = 1;
if (currIndex === this.list.length - 1) { if (index === list.value.length - 1) {
actTime = zeroTime + 24 * 3600 * 1000; actTime = zeroTime + 24 * 3600 * 1000;
} else { } else {
actTime = zeroTime + this.list[currIndex + 1].timeLine * 3600 * 1000; actTime = zeroTime + list.value[index + 1].timeLine * 3600 * 1000;
} }
} }
this.currHour = this.list[this.currIndex].timeLine; currHour.value = list.value[currIndex.value].timeLine;
this.diffSeconds = Math.floor((actTime - currTime) / 1000); diffSeconds.value = Math.floor((actTime - currTime) / 1000);
this.interval = setInterval(() => { interval.value = setInterval(() => {
this.diffSeconds--; diffSeconds.value--;
}, 1000); }, 1000);
}, }
getListByDay() {
this.list = this.data?.options?.list || []; function getListByDay() {
if (!this.list.length) { list.value = props.data?.options?.list || [];
this.goodsList = []; if (!list.value.length) {
goodsList.value = [];
return; return;
} }
this.goodsList = this.list[0]?.seckillGoodsList || []; goodsList.value = list.value[0]?.seckillGoodsList || [];
if (this.goodsList.length) { if (goodsList.value.length) {
this.countDown(this.currIndex); countDown(currIndex.value);
} }
}, }
},
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.seckill { .seckill {

View File

@@ -44,24 +44,19 @@
</div> </div>
</template> </template>
<script> <script setup>
export default { import { ref } from 'vue'
name: "forYour",
data() { defineOptions({ name: 'forYour' })
return {
current: "", defineProps({
};
},
props: {
data: { data: {
type: Object, type: Object,
default: () => ({}), default: () => ({}),
}, },
}, })
mounted() {}, const current = ref('')
methods: {},
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -43,31 +43,28 @@
</div> </div>
</template> </template>
<script> <script setup>
export default { import { onMounted, ref } from "vue";
name: "goods",
props: { defineOptions({ name: "goods" });
const props = defineProps({
paramData: { paramData: {
type: Object, type: Object,
default: () => ({}), default: () => ({}),
}, },
}, });
data() {
return { const current = ref("");
current: "",
}; onMounted(() => {
}, current.value = props.paramData.options.labels[0].___index;
watch: {}, });
mounted() {
this.current = this.paramData.options.labels[0].___index; // 筛选出当前分类下的商品
}, function handleClickLabel(val, index) {
methods: { current.value = index;
// 筛选出当前分类下的商品 }
handleClickLabel(val, index) {
this.current = index;
},
},
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -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", defineOptions({ name: "mixModel" });
data() {
return {} defineProps({
},
props: {
data: { data: {
type: Object, type: Object,
default: () => ({}) default: () => ({})
} }
}, })
components: {
left,
right
},
mounted() {
},
methods: {}
}
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -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">

View File

@@ -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">

View File

@@ -11,22 +11,15 @@
</div> </div>
</template> </template>
<script> <script setup>
export default { defineOptions({ name: "oneRowThreeColumns" });
name: "oneRowThreeColumns",
props: { defineProps({
data: { data: {
type: Object, type: Object,
default: () => ({}) default: () => ({})
} }
}, });
data () {
return {
};
},
methods: {}
}
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -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: { defineProps({
data: { data: {
type: Object, type: Object,
default: () => ({}), default: () => ({}),
}, },
}, });
data() {
return {
flag:false,
tabIndex:0,
current: 0,
showModal: false,
selected: {}, // 已选数据
picModelFlag: false,
};
},
mounted(){
}, const router = useRouter();
methods: {
handleClick(val){ const flag = ref(false);
this.linkTo(val.url) 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">

View File

@@ -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,19 +86,24 @@
</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 () {
return { const emit = defineEmits(['change']);
invoice: 1, // 1 电子普通发票 2 增值税专用发票
invoiceAvailable: false, const route = useRoute();
loading: false,
invoiceForm: { const invoice = ref(1); // 1 电子普通发票 2 增值税专用发票
const invoiceAvailable = ref(false);
const loading = ref(false);
const invoiceForm = ref({
receiptTitle: '', receiptTitle: '',
personalName: '', personalName: '',
companyName: '', companyName: '',
@@ -110,13 +115,12 @@ export default {
companyPhone: '', companyPhone: '',
bankName: '', bankName: '',
bankAccount: '' bankAccount: ''
}, });
type: 1 const type = ref(1);
};
}, const formRef = ref(null);
props: ['invoiceData'],
computed: { const formRules = computed(() => {
formRules () {
const receiptPhoneRules = [ const receiptPhoneRules = [
{ required: true, message: '请填写收票人手机', trigger: 'blur' }, { required: true, message: '请填写收票人手机', trigger: 'blur' },
{ pattern: mobile, message: '请填写正确的手机号码', trigger: 'blur' } { pattern: mobile, message: '请填写正确的手机号码', trigger: 'blur' }
@@ -140,7 +144,7 @@ export default {
} }
]; ];
if (this.invoice === 2) { if (invoice.value === 2) {
return { return {
companyName: [{ required: true, message: '请填写单位名称', trigger: 'blur' }], companyName: [{ required: true, message: '请填写单位名称', trigger: 'blur' }],
taxpayerId: [{ required: true, message: '请填写纳税人识别号', trigger: 'blur' }], taxpayerId: [{ required: true, message: '请填写纳税人识别号', trigger: 'blur' }],
@@ -157,76 +161,71 @@ export default {
receiptPhone: receiptPhoneRules, receiptPhone: receiptPhoneRules,
receiptEmail: receiptEmailRules receiptEmail: receiptEmailRules
}; };
if (this.type === 1) { if (type.value === 1) {
rules.personalName = [{ required: true, message: '请填写个人名称', trigger: 'blur' }]; rules.personalName = [{ required: true, message: '请填写个人名称', trigger: 'blur' }];
} else { } else {
rules.companyName = [{ required: true, message: '请填写单位名称', trigger: 'blur' }]; rules.companyName = [{ required: true, message: '请填写单位名称', trigger: 'blur' }];
rules.taxpayerId = [{ required: true, message: '请填写纳税人识别号', trigger: 'blur' }]; rules.taxpayerId = [{ required: true, message: '请填写纳税人识别号', trigger: 'blur' }];
} }
return rules; return rules;
} });
},
watch: { watch(() => props.invoiceData, (val) => {
invoiceData: {
handler (val) {
if (!val) return; if (!val) return;
this.invoiceForm = { ...this.invoiceForm, ...val }; invoiceForm.value = { ...invoiceForm.value, ...val };
if (!this.invoiceForm.receiptPhone) this.invoiceForm.receiptPhone = ''; if (!invoiceForm.value.receiptPhone) invoiceForm.value.receiptPhone = '';
if (!this.invoiceForm.receiptEmail) this.invoiceForm.receiptEmail = ''; if (!invoiceForm.value.receiptEmail) invoiceForm.value.receiptEmail = '';
if (!this.invoiceForm.receiptContent) this.invoiceForm.receiptContent = '商品明细'; if (!invoiceForm.value.receiptContent) invoiceForm.value.receiptContent = '商品明细';
if (!this.invoiceForm.companyAddress) this.invoiceForm.companyAddress = ''; if (!invoiceForm.value.companyAddress) invoiceForm.value.companyAddress = '';
if (!this.invoiceForm.companyPhone) this.invoiceForm.companyPhone = ''; if (!invoiceForm.value.companyPhone) invoiceForm.value.companyPhone = '';
if (!this.invoiceForm.bankName) this.invoiceForm.bankName = ''; if (!invoiceForm.value.bankName) invoiceForm.value.bankName = '';
if (!this.invoiceForm.bankAccount) this.invoiceForm.bankAccount = ''; if (!invoiceForm.value.bankAccount) invoiceForm.value.bankAccount = '';
if (!this.invoiceForm.personalName) this.invoiceForm.personalName = ''; if (!invoiceForm.value.personalName) invoiceForm.value.personalName = '';
if (!this.invoiceForm.companyName) this.invoiceForm.companyName = ''; if (!invoiceForm.value.companyName) invoiceForm.value.companyName = '';
const rt = val.receiptType; const rt = val.receiptType;
if (rt === 2 || rt === '2' || val.invoiceKind === 'VAT_SPECIAL') { if (rt === 2 || rt === '2' || val.invoiceKind === 'VAT_SPECIAL') {
this.invoice = 2; invoice.value = 2;
this.type = 2; type.value = 2;
} else { } else {
this.invoice = 1; invoice.value = 1;
if (val.taxpayerId) this.type = 2; if (val.taxpayerId) type.value = 2;
else this.type = 1; else type.value = 1;
} }
// 兼容后端 receiptTitle 存「个人/单位」,实际名称在 personalName/companyName;旧数据名称可能只在 receiptTitle // 兼容后端 receiptTitle 存「个人/单位」,实际名称在 personalName/companyName;旧数据名称可能只在 receiptTitle
const titleAsName = (t) => (t && t !== '个人' && t !== '单位' ? t : ''); const titleAsName = (t) => (t && t !== '个人' && t !== '单位' ? t : '');
if (this.invoice === 2) { if (invoice.value === 2) {
this.invoiceForm.companyName = val.companyName || titleAsName(val.receiptTitle) || ''; invoiceForm.value.companyName = val.companyName || titleAsName(val.receiptTitle) || '';
} else if (this.type === 2) { } else if (type.value === 2) {
this.invoiceForm.companyName = val.companyName || titleAsName(val.receiptTitle) || ''; invoiceForm.value.companyName = val.companyName || titleAsName(val.receiptTitle) || '';
this.invoiceForm.personalName = val.personalName || ''; invoiceForm.value.personalName = val.personalName || '';
} else { } else {
this.invoiceForm.personalName = val.personalName || titleAsName(val.receiptTitle) || ''; invoiceForm.value.personalName = val.personalName || titleAsName(val.receiptTitle) || '';
this.invoiceForm.companyName = val.companyName || ''; invoiceForm.value.companyName = val.companyName || '';
} }
if (this.invoice === 1) { if (invoice.value === 1) {
this.invoiceForm.companyAddress = ''; invoiceForm.value.companyAddress = '';
this.invoiceForm.companyPhone = ''; invoiceForm.value.companyPhone = '';
this.invoiceForm.bankName = ''; invoiceForm.value.bankName = '';
this.invoiceForm.bankAccount = ''; invoiceForm.value.bankAccount = '';
} }
if (this.invoice === 2) { if (invoice.value === 2) {
this.invoiceForm.personalName = ''; invoiceForm.value.personalName = '';
} }
}, }, { deep: true, immediate: true });
deep: true,
immediate: true function onHeaderTypeChange (val) {
if (invoice.value === 1 && val === 1 && invoiceForm.value.personalName === '个人') {
invoiceForm.value.personalName = '';
} }
}, nextTick(() => {
methods: { if (formRef.value) formRef.value.clearValidate();
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; function onInvoiceKindChange (val) {
this.invoiceForm = { type.value = val === 2 ? 2 : 1;
invoiceForm.value = {
receiptTitle: '', receiptTitle: '',
personalName: '', personalName: '',
companyName: '', companyName: '',
@@ -239,57 +238,57 @@ export default {
bankName: '', bankName: '',
bankAccount: '' bankAccount: ''
}; };
this.$nextTick(() => { nextTick(() => {
if (this.$refs.form) this.$refs.form.clearValidate(); if (formRef.value) formRef.value.clearValidate();
}); });
}, }
async save () { async function save () {
if (this.invoice === 2) { if (invoice.value === 2) {
this.invoiceForm.receiptContent = '商品明细'; invoiceForm.value.receiptContent = '商品明细';
} }
return await new Promise(resolve => { return await new Promise(resolve => {
this.$refs.form.validate(valid => resolve(valid)); formRef.value.validate(valid => resolve(valid));
}); });
}, }
async submit () { async function submit () {
if (!await this.save()) return; if (!await save()) return;
this.loading = true; loading.value = true;
const raw = JSON.parse(JSON.stringify(this.invoiceForm)); const raw = JSON.parse(JSON.stringify(invoiceForm.value));
if (this.invoice === 1) { if (invoice.value === 1) {
delete raw.companyAddress; delete raw.companyAddress;
delete raw.companyPhone; delete raw.companyPhone;
delete raw.bankName; delete raw.bankName;
delete raw.bankAccount; delete raw.bankAccount;
delete raw.invoiceAddress; delete raw.invoiceAddress;
if (this.type === 1) { if (type.value === 1) {
delete raw.taxpayerId; delete raw.taxpayerId;
} }
} else { } else {
delete raw.personalName; delete raw.personalName;
} }
const isCompanyTitle = this.invoice === 2 || this.type === 2; const isCompanyTitle = invoice.value === 2 || type.value === 2;
const titleName = isCompanyTitle const titleName = isCompanyTitle
? (raw.companyName != null ? String(raw.companyName).trim() : '') ? (raw.companyName != null ? String(raw.companyName).trim() : '')
: (raw.personalName != null ? String(raw.personalName).trim() : ''); : (raw.personalName != null ? String(raw.personalName).trim() : '');
const submit = { const submit = {
way: this.$route.query.way, way: route.query.way,
...raw, ...raw,
receiptType: this.invoice, receiptType: invoice.value,
receiptTitle: isCompanyTitle ? '单位' : '个人', receiptTitle: isCompanyTitle ? '单位' : '个人',
personalName: isCompanyTitle ? '' : titleName, personalName: isCompanyTitle ? '' : titleName,
companyName: isCompanyTitle ? titleName : '' companyName: isCompanyTitle ? titleName : ''
}; };
let receipt = await receiptSelect(submit); let receipt = await receiptSelect(submit);
if (receipt.success) { if (receipt.success) {
this.$emit('change', true); emit('change', true);
} }
this.loading = false; loading.value = false;
} }
}
}; defineExpose({ invoiceAvailable });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.inv-type { .inv-type {

View File

@@ -18,58 +18,56 @@
</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() {
return {
config,
showMap: false, // 地图显隐
mapSearch: "", // 地图搜索
map: null, // 初始化地图
autoComplete: null, // 初始化搜索方法
geocoder: null, // 初始化地理、坐标转化
positionPicker: null, // 地图拖拽选点
tips: [], //搜索关键字列表
addrContent: {}, // 回显地址信息
loading: false, // 加载状态
};
},
watch: {
mapSearch: function (val) {
this.searchOfMap(val);
},
},
methods: {
ok() {
if (this.addrContent && this.addrContent.regeocode) { const emit = defineEmits(["getAddress"]);
const showMap = ref(false); // 地图显隐
const mapSearch = ref(""); // 地图搜索
let map = null; // 初始化地图
let autoComplete = null; // 初始化搜索方法
let geocoder = null; // 初始化地理、坐标转化
let positionPicker = null; // 地图拖拽选点
const tips = ref([]); //搜索关键字列表
const addrContent = ref({}); // 回显地址信息
const loading = ref(false); // 加载状态
watch(mapSearch, function (val) {
searchOfMap(val);
});
function ok() {
if (addrContent.value && addrContent.value.regeocode) {
const params = { const params = {
cityCode: this.addrContent.regeocode.addressComponent.citycode, cityCode: addrContent.value.regeocode.addressComponent.citycode,
townName: this.addrContent.regeocode.addressComponent.township, townName: addrContent.value.regeocode.addressComponent.township,
}; };
handleRegion(params).then((res) => { handleRegion(params).then((res) => {
if (res.success) { if (res.success) {
this.addrContent.addr = res.result.name.replace(/,/g, " "); addrContent.value.addr = res.result.name.replace(/,/g, " ");
this.addrContent.addrId = res.result.id; addrContent.value.addrId = res.result.id;
this.loading = false; loading.value = false;
this.$emit("getAddress", this.addrContent); emit("getAddress", addrContent.value);
} }
}); });
} else { } else {
Message.error('未获取到坐标信息!请查看高德API配置是否正确') Message.error('未获取到坐标信息!请查看高德API配置是否正确')
} }
}, }
init() {
function init() {
AMapLoader.load({ AMapLoader.load({
key: this.config.aMapKey, // 申请好的Web端开发者Key,首次调用 load 时必填 key: config.aMapKey, // 申请好的Web端开发者Key,首次调用 load 时必填
version: "", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15 version: "", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
plugins: [ plugins: [
"AMap.ToolBar", "AMap.ToolBar",
@@ -85,68 +83,67 @@ export default {
}, },
}) })
.then((AMap) => { .then((AMap) => {
let that = this; map = new AMap.Map("map-container", {
this.map = new AMap.Map("map-container", {
zoom: 12, zoom: 12,
}); });
that.map.addControl(new AMap.ToolBar()); map.addControl(new AMap.ToolBar());
that.map.addControl(new AMap.Autocomplete()); map.addControl(new AMap.Autocomplete());
that.map.addControl(new AMap.PlaceSearch()); map.addControl(new AMap.PlaceSearch());
that.map.addControl(new AMap.Geocoder()); map.addControl(new AMap.Geocoder());
// 实例化Autocomplete // 实例化Autocomplete
let autoOptions = { let autoOptions = {
city: "全国", city: "全国",
}; };
that.autoComplete = new AMap.Autocomplete(autoOptions); // 搜索 autoComplete = new AMap.Autocomplete(autoOptions); // 搜索
that.geocoder = new AMap.Geocoder(autoOptions); geocoder = new AMap.Geocoder(autoOptions);
that.positionPicker = new AMapUI.PositionPicker({ positionPicker = new AMapUI.PositionPicker({
// 拖拽选点 // 拖拽选点
mode: "dragMap", mode: "dragMap",
map: that.map, map: map,
}); });
that.positionPicker.start(); positionPicker.start();
/** /**
* *
* 所有回显数据,都在positionResult里面 * 所有回显数据,都在positionResult里面
* 需要字段可以查找 * 需要字段可以查找
* *
*/ */
that.positionPicker.on("success", function (positionResult) { positionPicker.on("success", function (positionResult) {
that.addrContent = positionResult; addrContent.value = positionResult;
}); });
}) })
.catch((e) => { .catch((e) => {
console.warn('地图组件加载失败', e); console.warn('地图组件加载失败', e);
}); });
}, }
searchOfMap(val) {
function searchOfMap(val) {
// 地图搜索 // 地图搜索
let that = this; autoComplete.search(val, function (status, result) {
this.autoComplete.search(val, function (status, result) {
// 搜索成功时,result即是对应的匹配数据 // 搜索成功时,result即是对应的匹配数据
if (status == "complete" && result.info == "OK") { if (status == "complete" && result.info == "OK") {
that.tips = result.tips; tips.value = result.tips;
} else { } else {
that.tips = []; tips.value = [];
} }
}); });
}, }
selectAddr(location) {
function selectAddr(location) {
// 选择坐标 // 选择坐标
if (!location) { if (!location) {
Message.warning("请选择正确点位"); Message.warning("请选择正确点位");
return false; return false;
} }
const lnglat = [location.lng, location.lat]; const lnglat = [location.lng, location.lat];
this.positionPicker.start(lnglat); positionPicker.start(lnglat);
}, }
},
mounted() { onMounted(() => {
this.init(); init();
}, });
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
#map-container { #map-container {

View File

@@ -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 }, const emit = defineEmits(['callback'])
data() {
return { const enableMap = ref(false)
aMapSwitch, const mapDefault = ref("select")
enableMap: false, const data = reactive({
mapDefault: "select",
data: {
province: [], //省 province: [], //省
city: [], //市 city: [], //市
area: [], //区 area: [], //区
street: [], //街道 street: [], //街道
}, })
chiosend: [], const chiosend = ref([])
}; onMounted(() => {
}, chiosend.value = new Array(4).fill("");
mounted() { })
this.chiosend = new Array(4).fill(""); function open() {
enableMap.value = true
init({ id: 0 }, 'province');
}
}, defineExpose({ open })
methods: {
open() {
this.enableMap = true
this.init({ id: 0 }, 'province');
},
changeMap(val) {
this.mapDefault = val
function changeMap(val) {
mapDefault.value = val
}
}, function init(val, level = 'province', index) {
init(val, level = 'province', index) {
if (level == 0) { if (level == 0) {
// 说明选择到了街道,将街道id存入数组 // 说明选择到了街道,将街道id存入数组
this.chiosend.splice(3, 1, val); chiosend.value.splice(3, 1, val);
} }
else { else {
API_Setup.getRegion(val.id).then((res) => { API_Setup.getRegion(val.id).then((res) => {
if (res.result.length && val.id !== 0) { if (res.result.length && val.id !== 0) {
this.chiosend[index] = val chiosend.value[index] = val
} }
else if(!res.result.length){ else if (!res.result.length) {
this.chiosend[index] = val chiosend.value[index] = val
} }
this.data[level] = res.result; data[level] = res.result;
if (level == 'city') { if (level == 'city') {
this.data.area = [] data.area = []
this.data.street = [] data.street = []
this.chiosend.splice(1, 3, "","",""); chiosend.value.splice(1, 3, "", "", "");
} }
if (level == 'area') { if (level == 'area') {
this.data.street = [] data.street = []
this.chiosend.splice(2, 2, "",""); chiosend.value.splice(2, 2, "", "");
} }
if (level == 'street') { if (level == 'street') {
this.chiosend.splice(3, 1, ""); chiosend.value.splice(3, 1, "");
} }
}); });
} }
}, }
getAddress(center) {
this.$emit('callback', { function getAddress(center) {
type: this.mapDefault, emit('callback', {
type: mapDefault.value,
data: center data: center
}) })
this.enableMap = false; enableMap.value = false;
}, }
// 选择完成
finished() { // 选择完成
if(!this.chiosend[0]){ function finished() {
if (!chiosend.value[0]) {
Message.error("请选择地址") Message.error("请选择地址")
return return
} }
const params = this.chiosend.filter((item) => item!=="" && item.value !== ""); const params = chiosend.value.filter((item) => item !== "" && item.value !== "");
this.enableMap = false; enableMap.value = false;
this.$emit('callback', { emit('callback', {
type: this.mapDefault, type: mapDefault.value,
data: params data: params
}) })
},
},
} }
</script> </script>

View File

@@ -9,28 +9,27 @@
></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 { const props = defineProps(['addressId']);
data: [], // 地区数据 const emit = defineEmits(['selected']);
addr: [] // 已选数据
}; const data = ref([]); // 地区数据
}, const addr = ref([]); // 已选数据
props: ['addressId'],
mounted () {}, function change (val, selectedData) { // 选择地区
methods: {
change (val, selectedData) { // 选择地区
/** /**
* @returns [regionId,region] * @returns [regionId,region]
*/ */
this.$emit('selected', [ emit('selected', [
val, val,
selectedData[selectedData.length - 1].__label.split('/') selectedData[selectedData.length - 1].__label.split('/')
]); ]);
}, }
loadData (item, callback) { // 加载数据
function loadData (item, callback) { // 加载数据
item.loading = true; item.loading = true;
getRegion(item.value).then((res) => { getRegion(item.value).then((res) => {
if (res.result.length <= 0) { if (res.result.length <= 0) {
@@ -58,11 +57,12 @@ export default {
callback(); callback();
} }
}); });
}, }
async init () { // 初始化地图数据
let data = await getRegion(0); async function init () { // 初始化地图数据
let res = await getRegion(0);
let arr = []; let arr = [];
data.result.forEach((item) => { res.result.forEach((item) => {
let obj; let obj;
// 台湾省做处理 // 台湾省做处理
if (item.name === '台湾省') { if (item.name === '台湾省') {
@@ -80,19 +80,20 @@ export default {
} }
arr.push(obj); arr.push(obj);
}); });
this.data = arr; data.value = arr;
console.warn('init'); console.warn('init');
}, }
async reviewData () {
async function reviewData () {
// 数据回显 // 数据回显
let addr = JSON.parse(JSON.stringify(this.addressId.split(','))); let addrArr = JSON.parse(JSON.stringify(props.addressId.split(',')));
let length = addr.length; let length = addrArr.length;
let data = await getRegion(0); let res = await getRegion(0);
let arr0 = []; let arr0 = [];
let arr1 = []; let arr1 = [];
let arr2 = []; let arr2 = [];
// 第一级数据 // 第一级数据
data.result.forEach((item) => { res.result.forEach((item) => {
let obj; let obj;
// 台湾省做处理 // 台湾省做处理
if (item.name === '台湾省') { if (item.name === '台湾省') {
@@ -112,36 +113,37 @@ export default {
}); });
// 根据选择的数据来加载数据列表 // 根据选择的数据来加载数据列表
if (length > 0) { if (length > 0) {
let children = await getRegion(addr[0]); let children = await getRegion(addrArr[0]);
children = this.handleData(children.result); children = handleData(children.result);
arr0.forEach((e) => { arr0.forEach((e) => {
if (e.value === addr[0]) { if (e.value === addrArr[0]) {
e.children = arr1 = children; e.children = arr1 = children;
} }
}); });
} }
if (length > 1) { if (length > 1) {
let children = await getRegion(addr[1]); let children = await getRegion(addrArr[1]);
children = this.handleData(children.result); children = handleData(children.result);
arr1.forEach((e) => { arr1.forEach((e) => {
if (e.value === addr[1]) { if (e.value === addrArr[1]) {
e.children = arr2 = children; e.children = arr2 = children;
} }
}); });
} }
if (length > 2) { if (length > 2) {
let children = await getRegion(addr[2]); let children = await getRegion(addrArr[2]);
children = this.handleData(children.result); children = handleData(children.result);
arr2.forEach((e) => { arr2.forEach((e) => {
if (e.value === addr[2]) { if (e.value === addrArr[2]) {
e.children = children; e.children = children;
} }
}); });
} }
this.data = arr0; data.value = arr0;
this.addr = addr; addr.value = addrArr;
}, }
handleData (data) {
function handleData (data) {
// 处理接口数据 // 处理接口数据
let item = []; let item = [];
data.forEach((child) => { data.forEach((child) => {
@@ -162,21 +164,15 @@ export default {
} }
}); });
return item; return item;
} }
},
watch: { watch(() => props.addressId, (v) => {
addressId: {
handler: function (v) {
if (v) { if (v) {
this.reviewData(); reviewData();
} else { } else {
this.init(); init();
} }
}, }, { immediate: true });
immediate: true
}
}
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
</style> </style>

View File

@@ -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>

View File

@@ -90,14 +90,17 @@
</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: {
const props = defineProps({
showAlways: { showAlways: {
// 总是显示下拉分类 // 总是显示下拉分类
default: false, default: false,
@@ -122,53 +125,53 @@ export default {
default: false, default: false,
type: Boolean, type: Boolean,
}, },
useClass:{ useClass: {
type:null, type: null,
default:'' default: '',
}
}, },
data() { });
return {
panel: false, // 二级分类展示 const router = useRouter();
panelIndex: -1, const appStore = useAppStore();
panelData: [], // 二级分类数据
showFirstList: false, // 始终展示一级列表 const panel = ref(false); // 二级分类展示
cateList: [], // 商品分类 const panelIndex = ref(-1);
}; const panelData = ref([]); // 二级分类数据
}, const showFirstList = ref(false); // 始终展示一级列表
computed: { const cateList = ref([]); // 商品分类
navList() {
// 导航列表 // 导航列表
const navList = computed(() => {
if (storage.getItem("navList")) { if (storage.getItem("navList")) {
return JSON.parse(storage.getItem("navList")); return JSON.parse(storage.getItem("navList"));
} }
return { list: [] }; return { list: [] };
}, });
},
methods: { function showFirstLists() {
showFirstLists(){ showFirstList.value = true;
this.showFirstList = true; if (
if(
localStorage.getItem("category") && localStorage.getItem("category") &&
localStorage.getItem("category_expiration_time") localStorage.getItem("category_expiration_time")
) { ) {
// this.getCate(); // this.getCate();
// 如果缓存过期,则获取最新的信息 // 如果缓存过期,则获取最新的信息
if (new Date() > localStorage.getItem("category_expiration_time")) { if (new Date() > localStorage.getItem("category_expiration_time")) {
this.getCate(); getCate();
return; return;
} }
this.cateList = JSON.parse(localStorage.getItem("category")); cateList.value = JSON.parse(localStorage.getItem("category"));
// Message.info(cateList) // Message.info(cateList)
} }
}, }
getCate() {
function getCate() {
// 获取分类数据 // 获取分类数据
if (this.hover) return false; if (props.hover) return false;
getCategory(0).then((res) => { getCategory(0).then((res) => {
if (res.success) { if (res.success) {
this.cateList = res.result; cateList.value = res.result;
this.$store.commit("SET_CATEGORY", res.result); appStore.setCategory(res.result);
// 过期时间 // 过期时间
var expirationTime = new Date().setHours(new Date().getHours() + 1); var expirationTime = new Date().setHours(new Date().getHours() + 1);
// 存放过期时间 // 存放过期时间
@@ -179,19 +182,22 @@ export default {
}).catch((err) => { }).catch((err) => {
console.warn('获取分类失败', err); console.warn('获取分类失败', err);
}); });
}, }
showDetail(index) {
function showDetail(index) {
// 展示全部分类 // 展示全部分类
this.panel = true; panel.value = true;
this.panelIndex = index; panelIndex.value = index;
this.panelData = this.cateList[index].children; panelData.value = cateList.value[index].children;
}, }
closePanel() {
this.showFirstList = false; function closePanel() {
this.panel = false; showFirstList.value = false;
this.panelIndex = -1; panel.value = false;
}, panelIndex.value = -1;
goGoodsList(id, secondId, firstId) { }
function goGoodsList(id, secondId, firstId) {
// 分类共有三级,传全部分类过去 // 分类共有三级,传全部分类过去
const arr = [firstId, secondId, id]; const arr = [firstId, secondId, id];
if (!arr[1]) { if (!arr[1]) {
@@ -200,28 +206,27 @@ export default {
if (!arr[0]) { if (!arr[0]) {
arr.shift(); arr.shift();
} }
this.$router.push({ router.push({
path: "/goodsList", path: "/goodsList",
query: { categoryId: arr.toString() }, query: { categoryId: arr.toString() },
}); });
}, }
},
mounted() { onMounted(() => {
if ( if (
localStorage.getItem("category") && localStorage.getItem("category") &&
localStorage.getItem("category_expiration_time") localStorage.getItem("category_expiration_time")
) { ) {
// 如果缓存过期,则获取最新的信息 // 如果缓存过期,则获取最新的信息
if (new Date() > localStorage.getItem("category_expiration_time")) { if (new Date() > localStorage.getItem("category_expiration_time")) {
this.getCate(); getCate();
return; return;
} }
this.cateList = JSON.parse(localStorage.getItem("category")); cateList.value = JSON.parse(localStorage.getItem("category"));
} else { } else {
this.getCate(); getCate();
} }
}, });
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -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,22 +116,29 @@
</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', defineOptions({ name: 'GoodsClassNav' });
data () {
return { const emit = defineEmits(['getParams']);
tabBar: {
const route = useRoute();
const router = useRouter();
const appStore = useAppStore();
const tabBar = ref({
name: '', name: '',
children: [], children: [],
first: null, first: null,
second: null second: null
}, });
showTagCount: 5, // 展示的搜索项数量 const showTagCount = ref(5); // 展示的搜索项数量
tagsContent: [ const tagsContent = ref([
// 标签 // 标签
{ {
key: '品牌', key: '品牌',
@@ -139,28 +146,23 @@ export default {
show: true, show: true,
values: [] values: []
} }
], ]);
selectedItem: [], // 已选分类集合 顶部展示 const selectedItem = ref([]); // 已选分类集合 顶部展示
brandIds: [], // 品牌id合集 const brandIds = ref([]); // 品牌id合集
params: {}, // 请求参数 const params = reactive({}); // 请求参数
rowOverflow: {} // 一行排不下时才显示更多 const rowOverflow = ref({}); // 一行排不下时才显示更多
}; const rootEl = ref(null); // 根元素,用于测量行溢出
},
computed: { const cateList = computed(() => appStore.category || []); // 商品分类
cateList () { // 商品分类
return this.$store.state.category || [] // 监听已选条件,来调用列表接口
} watch(selectedItem, (val) => {
},
watch: {
selectedItem: {
// 监听已选条件,来调用列表接口
handler (val) {
let classification = []; let classification = [];
this.params.brandId = '' params.brandId = ''
this.params.prop = '' params.prop = ''
val.forEach((item) => { val.forEach((item) => {
if (item.type === '品牌') { if (item.type === '品牌') {
this.params.brandId = this.brandIds.join('@'); params.brandId = brandIds.value.join('@');
} else { } else {
const nameArr = item.name.split('、'); const nameArr = item.name.split('、');
nameArr.forEach((name) => { nameArr.forEach((name) => {
@@ -168,40 +170,36 @@ export default {
}); });
} }
}); });
this.params.prop = classification.join('@'); params.prop = classification.join('@');
this.getFilterList(this.params); getFilterList(params);
this.$emit('getParams', this.params); emit('getParams', params);
}, }, { deep: true });
deep: true
}, // 监听路由
'$route': { // 监听路由 watch(() => route.query, () => {
handler (val, oVal) { if (route.query.categoryId) {
if (this.$route.query.categoryId) { let cateId = route.query.categoryId.split(',')
let cateId = this.$route.query.categoryId.split(',') Object.assign(params, route.query)
Object.assign(this.params, this.$route.query) params.categoryId = cateId[cateId.length - 1]
this.params.categoryId = cateId[cateId.length - 1]
} else { } else {
Object.assign(this.params, this.$route.query) Object.assign(params, route.query)
} }
this.getFilterList(this.params) getFilterList(params)
this.getNav() getNav()
}, }, { deep: true });
deep: true
} function getNav () { // 获取商品分类,分类下展示
}, if (!route.query.categoryId) return
methods: { if (!cateList.value.length) { // 商品分类存储在localstorage,接口未调用成功前再次刷新数据
getNav () { // 获取商品分类,分类下展示
if (!this.$route.query.categoryId) return
if (!this.cateList.length) { // 商品分类存储在localstorage,接口未调用成功前再次刷新数据
setTimeout(() => { setTimeout(() => {
this.getNav() getNav()
}, 500) }, 500)
return return
} }
const arr = this.$route.query.categoryId.split(',') const arr = route.query.categoryId.split(',')
const source = this.cateList.find((e) => e.id === arr[0]) const source = cateList.value.find((e) => e.id === arr[0])
if (!source) return if (!source) return
const tabBar = { const newTabBar = {
id: source.id, id: source.id,
name: source.name, name: source.name,
parentId: source.parentId, parentId: source.parentId,
@@ -210,152 +208,161 @@ export default {
second: null, second: null,
} }
if (arr.length > 1) { if (arr.length > 1) {
tabBar.first = tabBar.children.find((e) => e.id === arr[1]) || null newTabBar.first = newTabBar.children.find((e) => e.id === arr[1]) || null
} }
if (arr.length > 2 && tabBar.first && tabBar.first.children) { if (arr.length > 2 && newTabBar.first && newTabBar.first.children) {
tabBar.second = tabBar.first.children.find((e) => e.id === arr[2]) || null newTabBar.second = newTabBar.first.children.find((e) => e.id === arr[2]) || null
} }
this.tabBar = tabBar tabBar.value = newTabBar
}, }
cateClick (item, index) { // 点选分类
function cateClick (item, index) { // 点选分类
switch (index) { switch (index) {
case 1: case 1:
this.$router.push({ router.push({
path: '/goodsList', path: '/goodsList',
query: {categoryId: item.id} query: {categoryId: item.id}
}) })
break; break;
case 2: case 2:
this.$router.push({ router.push({
path: '/goodsList', path: '/goodsList',
query: {categoryId: [item.parentId, item.id].toString()} query: {categoryId: [item.parentId, item.id].toString()}
}) })
break; break;
case 3: case 3:
this.$router.push({ router.push({
path: '/goodsList', path: '/goodsList',
query: {categoryId: [this.tabBar.id, item.parentId, item.id].toString()} query: {categoryId: [tabBar.value.id, item.parentId, item.id].toString()}
}) })
break; break;
} }
}, }
isGroupSelected (index) {
const group = this.tagsContent[index]; function isGroupSelected (index) {
const group = tagsContent.value[index];
if (!group) return false; if (!group) return false;
return this.selectedItem.some((selected) => selected.type === group.key); return selectedItem.value.some((selected) => selected.type === group.key);
}, }
selectAll (index) {
const group = this.tagsContent[index]; function selectAll (index) {
const group = tagsContent.value[index];
if (!group) return; if (!group) return;
const selectedIndex = this.selectedItem.findIndex((selected) => selected.type === group.key); const selectedIndex = selectedItem.value.findIndex((selected) => selected.type === group.key);
if (selectedIndex === -1) return; if (selectedIndex === -1) return;
this.cancelSelected(this.selectedItem[selectedIndex], selectedIndex); cancelSelected(selectedItem.value[selectedIndex], selectedIndex);
}, }
selectBrand (item, index) {
const type = this.tagsContent[index].key; function selectBrand (item, index) {
if (this.selectedItem.some((selected) => selected.type === type)) { const type = tagsContent.value[index].key;
if (selectedItem.value.some((selected) => selected.type === type)) {
return; return;
} }
this.selectedItem.push({ selectedItem.value.push({
type, type,
name: item name: item
}); });
this.tagsContent[index].show = false; tagsContent.value[index].show = false;
if (index === 0) { if (index === 0) {
this.appendBrandIdsByNames([item]); appendBrandIdsByNames([item]);
} }
}, }
cancelSelected (item, index) { function cancelSelected (item, index) {
// 顶部栏 取消已选中的项 // 顶部栏 取消已选中的项
this.selectedItem.splice(index, 1); selectedItem.value.splice(index, 1);
this.tagsContent.forEach((tag, index) => { tagsContent.value.forEach((tag, index) => {
if (tag.key === item.type) { if (tag.key === item.type) {
tag.show = true; tag.show = true;
tag.more = false; tag.more = false;
} }
}); });
if (item.type === '品牌') { if (item.type === '品牌') {
this.brandIds = []; brandIds.value = [];
} }
}, }
moreBrand (index) {
function moreBrand (index) {
// 更多按钮 // 更多按钮
const flag = !this.tagsContent[index].more const flag = !tagsContent.value[index].more
this.tagsContent[index]['more'] = flag tagsContent.value[index]['more'] = flag
this.$nextTick(() => this.measureRows()) nextTick(() => measureRows())
}, }
measureRows () {
if (!this.$el) return function measureRows () {
if (!rootEl.value) return
const next = {} const next = {}
const brand = this.$el.querySelector('.brand ul') const brand = rootEl.value.querySelector('.brand ul')
if (brand && brand.offsetParent) { if (brand && brand.offsetParent) {
next[0] = !!(this.tagsContent[0] && this.tagsContent[0].more) || brand.scrollHeight > brand.clientHeight + 1 next[0] = !!(tagsContent.value[0] && tagsContent.value[0].more) || brand.scrollHeight > brand.clientHeight + 1
} }
this.$el.querySelectorAll('.other .list').forEach((list) => { rootEl.value.querySelectorAll('.other .list').forEach((list) => {
if (!list.offsetParent) return if (!list.offsetParent) return
const index = Number(list.dataset.index) const index = Number(list.dataset.index)
const tag = this.tagsContent[index] const tag = tagsContent.value[index]
next[index] = !!(tag && tag.more) || list.scrollHeight > list.clientHeight + 1 next[index] = !!(tag && tag.more) || list.scrollHeight > list.clientHeight + 1
}) })
const prev = this.rowOverflow const prev = rowOverflow.value
const keys = new Set([...Object.keys(prev), ...Object.keys(next)]) const keys = new Set([...Object.keys(prev), ...Object.keys(next)])
const changed = [...keys].some((key) => !!prev[key] !== !!next[key]) const changed = [...keys].some((key) => !!prev[key] !== !!next[key])
if (changed) this.rowOverflow = next if (changed) rowOverflow.value = next
}, }
appendBrandIdsByNames (names) {
function appendBrandIdsByNames (names) {
const selectedNames = names || []; const selectedNames = names || [];
const brands = this.tagsContent[0] && this.tagsContent[0].values ? this.tagsContent[0].values : []; const brands = tagsContent.value[0] && tagsContent.value[0].values ? tagsContent.value[0].values : [];
brands.forEach((val) => { brands.forEach((val) => {
if (selectedNames.includes(val.name) && val.value && !this.brandIds.includes(val.value)) { if (selectedNames.includes(val.name) && val.value && !brandIds.value.includes(val.value)) {
this.brandIds.push(val.value); brandIds.value.push(val.value);
} }
}); });
}, }
getFilterList (params) {
function getFilterList (params) {
// 筛选、分类 列表 // 筛选、分类 列表
APIGoods.filterList(params).then((res) => { APIGoods.filterList(params).then((res) => {
if (res.success) { if (res.success) {
const data = res.result; const data = res.result;
const hiddenKeys = new Set(this.selectedItem.map((item) => item.type)); const hiddenKeys = new Set(selectedItem.value.map((item) => item.type));
this.tagsContent = [{ tagsContent.value = [{
key: '品牌', key: '品牌',
more: false, more: false,
show: !hiddenKeys.has('品牌'), show: !hiddenKeys.has('品牌'),
values: data.brands || [] values: data.brands || []
}].concat(data.paramOptions || []); }].concat(data.paramOptions || []);
this.tagsContent.forEach((item) => { tagsContent.value.forEach((item) => {
item.show = !hiddenKeys.has(item.key); item.show = !hiddenKeys.has(item.key);
item.more = false; item.more = false;
}); });
this.$nextTick(() => this.measureRows()) nextTick(() => 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 {
Object.assign(this.params, this.$route.query)
}
this.getFilterList(this.params);
this.getNav();
window.addEventListener('resize', this.measureRows)
},
beforeUnmount () {
window.removeEventListener('resize', this.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">

View File

@@ -27,10 +27,13 @@
</div> </div>
</template> </template>
<script> <script setup>
export default { import { computed, useSlots } from "vue";
name: "UserCenterLayout", import { useRouter } from "vue-router";
props: {
defineOptions({ name: "UserCenterLayout" });
const props = defineProps({
title: { title: {
type: String, type: String,
default: "", default: "",
@@ -51,25 +54,26 @@ export default {
type: [String, Object], type: [String, Object],
default: "", default: "",
}, },
}, });
emits: ["tab-change", "more"],
computed: { const emit = defineEmits(["tab-change", "more"]);
showToolbar() {
return this.tabs.length || this.moreText || this.$slots.extra; const router = useRouter();
}, const slots = useSlots();
},
methods: { const showToolbar = computed(() => {
handleMore() { return props.tabs.length || props.moreText || slots.extra;
this.$emit("more"); });
if (!this.moreTo) return;
if (typeof this.moreTo === "string") { function handleMore() {
this.$router.push(this.moreTo); emit("more");
if (!props.moreTo) return;
if (typeof props.moreTo === "string") {
router.push(props.moreTo);
} else { } else {
this.$router.push(this.moreTo); router.push(props.moreTo);
} }
}, }
},
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -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 }, const props = defineProps({
props: {
// 传入数据,判断是登录、注册、修改密码 // 传入数据,判断是登录、注册、修改密码
verifyType: { verifyType: {
defalut: 'LOGIN', defalut: 'LOGIN',
type: String type: String
} }
}, });
data () {
return { const emit = defineEmits(['change']);
show: false, // 验证码显隐
type: 'LOGIN', // 请求类型 const show = ref(false); // 验证码显隐
data: { // 验证码数据 const type = ref('LOGIN'); // 请求类型
const data = ref({ // 验证码数据
backImage: '', backImage: '',
slidingImage: '', slidingImage: '',
originalHeight: 150, originalHeight: 150,
originalWidth: 300, originalWidth: 300,
sliderWidth: 60, sliderWidth: 60,
sliderHeight: 60 sliderHeight: 60
}, });
distance: 0, // 拼图移动距离 const distance = ref(0); // 拼图移动距离
flag: false, // 判断滑块是否按下 const flag = ref(false); // 判断滑块是否按下
downX: 0, // 鼠标按下位置 const downX = ref(0); // 鼠标按下位置
bgColor: '#04ad11', // 滑动背景颜色 const bgColor = ref('#04ad11'); // 滑动背景颜色
verifyText: '拖动滑块解锁' // 文字提示 const verifyText = ref('拖动滑块解锁'); // 文字提示
};
},
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) { // 鼠标按下事件,开始拖动滑块
this.distance = this.data.originalWidth - 43; function mouseDown (e) {
downX.value = e.clientX;
flag.value = true;
}
// 鼠标移动事件,计算距离
function mouseMove (e) {
if (flag.value) {
let offset = e.clientX - downX.value;
if (offset > data.value.originalWidth - 43) {
distance.value = data.value.originalWidth - 43;
} else if (offset < 0) { } else if (offset < 0) {
this.distance = 0; distance.value = 0;
} else { } else {
this.distance = offset; distance.value = offset;
} }
} }
}, }
// 鼠标抬起事件,验证是否正确 // 鼠标抬起事件,验证是否正确
mouseUp () { function mouseUp () {
if (!this.flag) return false; if (!flag.value) return false;
this.flag = false; flag.value = false;
let params = { let params = {
verificationEnums: this.type, verificationEnums: type.value,
xPos: this.distance xPos: distance.value
}; };
postVerifyImg(params).then(res => { postVerifyImg(params).then(res => {
if (res.success) { if (res.success) {
if (res.result) { if (res.result) {
this.bgColor = 'green'; bgColor.value = 'green';
this.verifyText = '解锁成功'; verifyText.value = '解锁成功';
this.$emit('change', { status: true, distance: this.distance }); emit('change', { status: true, distance: distance.value });
} else { } else {
this.bgColor = 'red'; bgColor.value = 'red';
this.verifyText = '解锁失败'; verifyText.value = '解锁失败';
let that = this;
setTimeout(() => { setTimeout(() => {
that.init(); init();
}, 1000); }, 1000);
this.$emit('change', { status: false, distance: this.distance }); emit('change', { status: false, distance: distance.value });
} }
} else { } else {
this.init() init()
} }
}).catch(()=>{ }).catch(()=>{
this.init() init()
}); });
}, }
close () { function close () {
this.show = false; show.value = false;
}, }
init () { // 初始化数据 function init () { // 初始化数据
this.flag = false; flag.value = false;
this.downX = 0; downX.value = 0;
this.distance = 0; distance.value = 0;
this.bgColor = '#04ad11'; bgColor.value = '#04ad11';
this.verifyText = '拖动滑块解锁'; verifyText.value = '拖动滑块解锁';
getVerifyImg(this.type).then(res => { getVerifyImg(type.value).then(res => {
if (res.result) { if (res.result) {
this.data = res.result; data.value = res.result;
this.show = true; show.value = true;
} else { } else {
Message.warning('请求失败请重试!') Message.warning('请求失败请重试!')
} }
}); });
} }
},
watch: { // 父组件通过 ref 调用 init/close
verifyType: { defineExpose({ init, close });
immediate: true,
handler: function (v) { watch(() => props.verifyType, (v) => {
this.type = v; type.value = v;
} }, { immediate: true });
}
}
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.verify-content{ .verify-content{

View File

@@ -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;

View File

@@ -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,55 +242,62 @@
</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
const goodsTotal = ref(0); // 商品数量
const checkedNum = ref(0); // 选中数量
const allChecked = ref(false); // 全选
const loading = ref(false); // 加载状态
const cartList = ref([]); // 购物车列表
const couponList = ref([]); // 优惠券列表
const priceDetailDTO = ref({}); // 价格明细
const skuList = ref([]); // sku列表
// 跳转商品详情
function goGoodsDetail(skuId, goodsId) {
let routeUrl = router.resolve({
path: "/goodsDetail", path: "/goodsDetail",
query: { skuId, goodsId }, query: { skuId, goodsId },
}); });
window.open(routeUrl.href, "_blank"); window.open(routeUrl.href, "_blank");
}, }
// 跳转店铺首页 // 跳转店铺首页
goShopPage(id) { function goShopPage(id) {
this.$router.push({ router.push({
path: "/merchant", path: "/merchant",
query: { id }, query: { id },
}); });
}, }
// 同步购物车商品收藏状态 // 同步购物车商品收藏状态
async syncCartCollectionStatus() { async function syncCartCollectionStatus() {
if (!this.Cookies.getItem("userInfo") || !this.cartList.length) return; if (!storage.getItem("userInfo") || !cartList.value.length) return;
const skuIds = []; const skuIds = [];
this.cartList.forEach((shop) => { cartList.value.forEach((shop) => {
(shop.skuList || []).forEach((goods) => { (shop.skuList || []).forEach((goods) => {
goods.isCollected = false; goods.isCollected = false;
if (goods.goodsSku && goods.goodsSku.id) { if (goods.goodsSku && goods.goodsSku.id) {
@@ -304,7 +311,7 @@ export default {
try { try {
const res = await APIMember.isCollection("GOODS", skuId); const res = await APIMember.isCollection("GOODS", skuId);
if (res.success && res.result) { if (res.success && res.result) {
this.cartList.forEach((shop) => { cartList.value.forEach((shop) => {
(shop.skuList || []).forEach((goods) => { (shop.skuList || []).forEach((goods) => {
if (goods.goodsSku && goods.goodsSku.id === skuId) { if (goods.goodsSku && goods.goodsSku.id === skuId) {
goods.isCollected = true; goods.isCollected = true;
@@ -317,10 +324,9 @@ export default {
} }
}) })
); );
this.$forceUpdate(); }
}, // 收藏 / 取消收藏商品
// 收藏 / 取消收藏商品 function handleCollect(goods) {
handleCollect(goods) {
const skuId = goods.goodsSku && goods.goodsSku.id; const skuId = goods.goodsSku && goods.goodsSku.id;
if (!skuId) return; if (!skuId) return;
if (goods.isCollected) { if (goods.isCollected) {
@@ -332,7 +338,6 @@ export default {
if (res.success) { if (res.success) {
goods.isCollected = false; goods.isCollected = false;
Message.success("取消收藏成功"); Message.success("取消收藏成功");
this.$forceUpdate();
} }
}); });
}, },
@@ -348,18 +353,17 @@ export default {
if (res.success) { if (res.success) {
goods.isCollected = true; goods.isCollected = true;
Message.success("收藏商品成功"); Message.success("收藏商品成功");
this.$forceUpdate();
} }
}); });
}, },
onCancel: () => {}, onCancel: () => {},
}); });
}, }
// 删除商品 // 删除商品
delGoods(id) { function delGoods(id) {
const idArr = []; const idArr = [];
if (!id) { if (!id) {
const list = this.cartList; const list = cartList.value;
list.forEach((shop) => { list.forEach((shop) => {
shop.skuList.forEach((goods) => { shop.skuList.forEach((goods) => {
if (goods.checked) { if (goods.checked) {
@@ -377,16 +381,16 @@ export default {
APICart.delCartGoods({ skuIds: idArr.toString() }).then((res) => { APICart.delCartGoods({ skuIds: idArr.toString() }).then((res) => {
if (res.success) { if (res.success) {
Message.success("删除成功"); Message.success("删除成功");
this.getCartList(); getCartList();
} else { } else {
Message.error(res.message); Message.error(res.message);
} }
}); });
}, },
}); });
}, }
// 清空购物车 // 清空购物车
clearCart() { function clearCart() {
Modal.confirm({ Modal.confirm({
title: "提示", title: "提示",
content: "确定要清空购物车吗?清空后不可恢复", content: "确定要清空购物车吗?清空后不可恢复",
@@ -394,21 +398,21 @@ export default {
APICart.clearCart().then((res) => { APICart.clearCart().then((res) => {
if (res.success) { if (res.success) {
Message.success("清空购物车成功"); Message.success("清空购物车成功");
this.getCartList(); getCartList();
} else { } else {
Message.error(res.message); Message.error(res.message);
} }
}); });
}, },
}); });
}, }
// 跳转支付页面 // 跳转支付页面
pay() { function pay() {
if (!this.checkedNum) { if (!checkedNum.value) {
Message.warning("请至少选择一件商品"); Message.warning("请至少选择一件商品");
return; return;
} }
const hasECoupon = this.cartList.some((shop) => const hasECoupon = cartList.value.some((shop) =>
(shop.skuList || []).some( (shop.skuList || []).some(
(goods) => goods.checked && isECoupon(goods.goodsSku && goods.goodsSku.goodsType) (goods) => goods.checked && isECoupon(goods.goodsSku && goods.goodsSku.goodsType)
) )
@@ -417,24 +421,24 @@ export default {
Message.warning("电子卡券请使用立即购买,不可通过购物车结算"); Message.warning("电子卡券请使用立即购买,不可通过购物车结算");
return; return;
} }
this.$router.push({ path: "/pay", query: { way: "CART" } }); router.push({ path: "/pay", query: { way: "CART" } });
}, }
// 展示优惠券 // 展示优惠券
showCoupon(storeId, index) { function showCoupon(storeId, index) {
this.couponAvailable = index; couponAvailable.value = index;
}, }
// 设置购买数量 // 设置购买数量
changeNum(val, id) { function changeNum(val, id) {
if (val) { if (val) {
APICart.setCartGoodsNum({ skuId: id, num: val }).then((res) => { APICart.setCartGoodsNum({ skuId: id, num: val }).then((res) => {
if (res.success) { if (res.success) {
this.getCartList(); getCartList();
} }
}); });
} }
}, }
// 设置商品选中状态 // 设置商品选中状态
async changeChecked(status, type, id) { async function changeChecked(status, type, id) {
const check = status ? 1 : 0; const check = status ? 1 : 0;
if (type === "all") { if (type === "all") {
// 全选 // 全选
@@ -447,10 +451,10 @@ export default {
await APICart.setCheckedGoods({ checked: check, skuId: id }); await APICart.setCheckedGoods({ checked: check, skuId: id });
} }
this.getCartList(); getCartList();
}, }
// 领取优惠券 // 领取优惠券
async receiveShopCoupon(item) { async function receiveShopCoupon(item) {
let res = await APIMember.receiveCoupon(item.id); let res = await APIMember.receiveCoupon(item.id);
if (res.success) { if (res.success) {
item["disabled"] = true; item["disabled"] = true;
@@ -458,48 +462,46 @@ export default {
} else { } else {
Message.error(res.message); Message.error(res.message);
} }
}, }
// 购物车列表 // 购物车列表
async getCartList() { async function getCartList() {
this.loading = true; loading.value = true;
try { try {
let res = await APICart.cartGoodsAll(); let res = await APICart.cartGoodsAll();
this.loading = false; loading.value = false;
if (res.success) { if (res.success) {
this.cartList = res.result.cartList; cartList.value = res.result.cartList;
this.priceDetailDTO = res.result.priceDetailDTO; priceDetailDTO.value = res.result.priceDetailDTO;
this.skuList = res.result.skuList; skuList.value = res.result.skuList;
this.checkedNum = 0; checkedNum.value = 0;
let allChecked = true; let allCheckedVal = true;
for (let k = 0; k < this.cartList.length; k++) { for (let k = 0; k < cartList.value.length; k++) {
let shop = this.cartList[k]; let shop = cartList.value[k];
let list = await APIMember.couponList({ storeId: shop.storeId }); let list = await APIMember.couponList({ storeId: shop.storeId });
shop.couponList.push(...list.result.records); shop.couponList.push(...list.result.records);
} }
for (let i = 0; i < this.skuList.length; i++) { for (let i = 0; i < skuList.value.length; i++) {
if (this.skuList[i].checked) { if (skuList.value[i].checked) {
this.checkedNum += this.skuList[i].num; checkedNum.value += skuList.value[i].num;
} else { } else {
allChecked = false; allCheckedVal = false;
} }
} }
this.$forceUpdate(); allChecked.value = allCheckedVal;
this.allChecked = allChecked; syncCartCollectionStatus();
this.syncCartCollectionStatus();
} }
} catch (error) { } catch (error) {
this.loading = false; loading.value = false;
} }
}, }
},
mounted() { onMounted(() => {
this.getCartList(); getCartList();
APICart.cartCount().then((res) => { APICart.cartCount().then((res) => {
// 购物车商品数量 // 购物车商品数量
if (res.success) this.goodsTotal = res.result; if (res.success) goodsTotal.value = res.result;
}); });
}, });
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -43,60 +43,61 @@
<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", getType: "FREE",
pageNumber: 1, pageNumber: 1,
pageSize: 20, pageSize: 20,
}, });
};
}, function getList() {
methods: { if (loading.value) return;
getList() { loading.value = true;
if (this.loading) return; couponList(params)
this.loading = true;
couponList(this.params)
.then((res) => { .then((res) => {
if (res && res.success && res.result) { if (res && res.success && res.result) {
this.list = res.result.records || []; list.value = res.result.records || [];
this.total = res.result.total || 0; total.value = res.result.total || 0;
} else { } else {
this.list = []; list.value = [];
this.total = 0; total.value = 0;
} }
}) })
.catch(() => { .catch(() => {
this.list = []; list.value = [];
this.total = 0; total.value = 0;
}) })
.finally(() => { .finally(() => {
this.loading = false; loading.value = false;
}); });
}, }
// 分页 改变页码 // 分页 改变页码
changePageNum(val) { function changePageNum(val) {
this.params.pageNumber = val; params.pageNumber = val;
this.getList(); getList();
}, }
// 分页 改变每页数 // 分页 改变每页数
changePageSize(val) { function changePageSize(val) {
this.params.pageNumber = 1; params.pageNumber = 1;
this.params.pageSize = val; params.pageSize = val;
this.getList(); getList();
}, }
// 领取优惠券 // 领取优惠券
receive(item) { function receive(item) {
if (this.isFinished(item)) return; if (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) {
@@ -106,11 +107,11 @@ export default {
okText: "立即登录", okText: "立即登录",
cancelText: "取消", cancelText: "取消",
onOk: () => { onOk: () => {
this.$router.push({ router.push({
path: "/login", path: "/login",
query: { query: {
rePath: this.$route.path, rePath: route.path,
query: JSON.stringify(this.$route.query), query: JSON.stringify(route.query),
}, },
}); });
}, },
@@ -127,10 +128,10 @@ export default {
cancelText: "立即使用", cancelText: "立即使用",
closable: true, closable: true,
onOk: () => { onOk: () => {
this.$router.push("/home/Coupons"); router.push("/home/Coupons");
}, },
onCancel: () => { onCancel: () => {
this.$router.push({ router.push({
path: "/goodsList", path: "/goodsList",
query: { promotionsId: item.id, promotionType: "COUPON" }, query: { promotionsId: item.id, promotionType: "COUPON" },
}); });
@@ -140,29 +141,28 @@ export default {
.catch((err) => { .catch((err) => {
console.warn('领取优惠券失败', err); console.warn('领取优惠券失败', err);
}); });
}, }
// 发行量已领完 // 发行量已领完
isFinished(item) { function isFinished(item) {
return Number(item.publishNum) > 0 && Number(item.receivedNum) >= Number(item.publishNum); return Number(item.publishNum) > 0 && Number(item.receivedNum) >= Number(item.publishNum);
}, }
// 使用门槛文案 // 使用门槛文案
thresholdText(item) { function thresholdText(item) {
const threshold = Number(item.consumeThreshold); const threshold = Number(item.consumeThreshold);
if (!threshold) return "无使用门槛"; if (!threshold) return "无使用门槛";
return `满${item.consumeThreshold}元可用`; return `满${item.consumeThreshold}元可用`;
}, }
// 券范围。店铺名为 platform 时是平台通用券 // 券范围。店铺名为 platform 时是平台通用券
scopeTag(type, storeName) { function scopeTag(type, storeName) {
if (type === "PORTION_GOODS") return "商品券"; if (type === "PORTION_GOODS") return "商品券";
if (type === "PORTION_GOODS_CATEGORY") return "分类券"; if (type === "PORTION_GOODS_CATEGORY") return "分类券";
if (storeName && storeName !== "platform") return "店铺券"; if (storeName && storeName !== "platform") return "店铺券";
return "通用券"; return "通用券";
}, }
},
mounted() { onMounted(() => {
this.getList(); getList();
}, });
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "../assets/styles/coupon.scss"; @import "../assets/styles/coupon.scss";

View File

@@ -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,26 +114,28 @@ 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 }, defineOptions({ name: 'ForgetPassword' });
data () {
return { const router = useRouter();
config, const appStore = useAppStore();
loading: false, // 加载状态 const logoImg = computed(() => appStore.logoImg);
loading1: false, // 第二步加载状态
formFirst: { // 手机验证码表单 const loading = ref(false); // 加载状态
const loading1 = ref(false); // 第二步加载状态
const formFirst = reactive({ // 手机验证码表单
// 注册表单 // 注册表单
mobile: '', mobile: '',
code: '' code: ''
}, });
form: { // 密码 const form = reactive({ // 密码
password: '', password: '',
oncePasd: '' oncePasd: ''
}, });
year: new Date().getFullYear(), // 当前年份 const year = new Date().getFullYear(); // 当前年份
step: 0, // 步骤 const step = ref(0); // 步骤
ruleInline: { const ruleInline = {
// 验证规则 // 验证规则
mobile: [ mobile: [
{ required: true, message: '请输入手机号码' }, { required: true, message: '请输入手机号码' },
@@ -144,86 +147,90 @@ export default {
], ],
code: [{ required: true, message: '请输入手机验证码' }], code: [{ required: true, message: '请输入手机验证码' }],
password: [{required: true, message: '密码不能为空'}, {pattern: RegExp.password, message: '密码不能少于6位'}] password: [{required: true, message: '密码不能为空'}, {pattern: RegExp.password, message: '密码不能少于6位'}]
}, };
verifyStatus: false, // 图片验证状态 const verifyStatus = ref(false); // 图片验证状态
verifyType: 'FIND_USER', // 图片验证类型 const verifyType = ref('FIND_USER'); // 图片验证类型
codeMsg: '发送验证码', // 验证码文字 const codeMsg = ref('发送验证码'); // 验证码文字
interval: '', // 定时器 const interval = ref(''); // 定时器
time: 60 // 倒计时时间 const time = ref(60); // 倒计时时间
};
}, const formFirstRef = ref(null);
methods: { const formRef = ref(null);
hideVerify() { const verifyRef = ref(null);
this.$refs.verify?.close?.();
}, function hideVerify() {
// 提交短信验证码,修改密码 verifyRef.value?.close?.();
next () { }
this.$refs.formFirst.validate((valid) => {
// 提交短信验证码,修改密码
function next () {
formFirstRef.value.validate((valid) => {
if (valid) { if (valid) {
this.loading = true; loading.value = true;
let data = JSON.parse(JSON.stringify(this.formFirst)); let data = JSON.parse(JSON.stringify(formFirst));
apiLogin.validateCode(data).then((res) => { apiLogin.validateCode(data).then((res) => {
this.loading = false; loading.value = false;
if (res.success) { if (res.success) {
// Message.success(''); // Message.success('');
this.step = 1; step.value = 1;
} else { } else {
Message.warning(res.message); Message.warning(res.message);
} }
}).catch(() => { this.loading = false; }); }).catch(() => { loading.value = false; });
} }
}); });
}, }
handleSubmit () { // 提交密码
this.$refs.form.validate(valid => { function handleSubmit () { // 提交密码
formRef.value.validate(valid => {
if (valid) { if (valid) {
let params = JSON.parse(JSON.stringify(this.form)); let params = JSON.parse(JSON.stringify(form));
if (params.password !== params.oncePasd) { if (params.password !== params.oncePasd) {
Message.warning('两次输入密码不一致'); Message.warning('两次输入密码不一致');
return; return;
}; };
params.mobile = this.formFirst.mobile; params.mobile = formFirst.mobile;
params.password = md5(params.password); params.password = md5(params.password);
delete params.oncePasd; delete params.oncePasd;
this.loading1 = true; loading1.value = true;
apiLogin.resetPassword(params).then(res => { apiLogin.resetPassword(params).then(res => {
this.loading1 = false; loading1.value = false;
if (res.success) { if (res.success) {
Message.success('修改密码成功'); Message.success('修改密码成功');
this.$router.push('login'); router.push('login');
} }
}).catch(() => { this.loading = false; }); }).catch(() => { loading.value = false; });
}; };
}); });
}, }
sendCode () { // 发送验证码
if (this.time === 60) { function sendCode () { // 发送验证码
if (this.formFirst.mobile === '') { if (time.value === 60) {
if (formFirst.mobile === '') {
Message.warning('请先填写手机号'); Message.warning('请先填写手机号');
return; return;
} }
if (!this.verifyStatus) { if (!verifyStatus.value) {
Message.warning('请先完成安全验证'); Message.warning('请先完成安全验证');
return; return;
} }
let params = { let params = {
mobile: this.formFirst.mobile, mobile: formFirst.mobile,
verificationEnums: 'FIND_USER' verificationEnums: 'FIND_USER'
}; };
sendSms(params).then(res => { sendSms(params).then(res => {
if (res.success) { if (res.success) {
Message.success('验证码发送成功'); Message.success('验证码发送成功');
let that = this; interval.value = setInterval(() => {
this.interval = setInterval(() => { time.value--;
that.time--; if (time.value === 0) {
if (that.time === 0) { time.value = 60;
that.time = 60; codeMsg.value = '重新发送';
that.codeMsg = '重新发送'; verifyStatus.value = false;
that.verifyStatus = false; clearInterval(interval.value);
clearInterval(that.interval);
} else { } else {
that.codeMsg = that.time; codeMsg.value = time.value;
} }
}, 1000); }, 1000);
} else { } else {
@@ -231,25 +238,24 @@ export default {
} }
}); });
} }
}, }
verifyChange (con) { // 验证通过
function verifyChange (con) { // 验证通过
if (!con.status) return; if (!con.status) return;
this.hideVerify(); hideVerify();
this.verifyStatus = true; verifyStatus.value = true;
}, }
verifyBtnClick () {
if (!this.verifyStatus) { function verifyBtnClick () {
this.$refs.verify?.init(); if (!verifyStatus.value) {
verifyRef.value?.init();
} }
} }
},
mounted () { onMounted(() => {
document.querySelector('.forget-password').style.height = window.innerHeight + 'px' document.querySelector('.forget-password').style.height = window.innerHeight + 'px'
this.$refs.formFirst.resetFields(); formFirstRef.value.resetFields();
}, });
watch: {
}
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -30,69 +30,72 @@
</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(); </script>
},
data () {
return {
goodsMsg: {}, // 商品信息
isLoading: false, // 加载状态
categoryBar: [], // 分类
takeDownSale:false, // 是否下架
}; <script setup>
}, import { ref } from "vue";
methods: { import { useRoute, useRouter } from "vue-router";
// 跳转首页或商品页面 import { Message } from "@/utils/message";
target(url){ import storage from "@/plugins/storage.js";
this.$router.push({path: url}) 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();
targetClickSku (val) {
this.getGoodsDetail(val); const goodsMsg = ref({}); // 商品信息
}, const isLoading = ref(false); // 加载状态
// 获取商品详情 const categoryBar = ref([]); // 分类
getGoodsDetail (val) { const takeDownSale = ref(false); // 是否下架
this.isLoading = true;
const params = val || this.$route.query; // 跳转首页或商品页面
function target(url){
router.push({path: url})
}
// 点击规格
function targetClickSku (val) {
getGoodsDetail(val);
}
// 获取商品详情
function getGoodsDetail (val) {
isLoading.value = true;
const params = val || route.query;
// 分销员id // 分销员id
let distributionId = let distributionId =
params && params.distributionId params && params.distributionId
? params.distributionId ? params.distributionId
: this.Cookies.getItem("distributionId"); : storage.getItem("distributionId");
// 如果有分销信息 // 如果有分销信息
if (distributionId) { if (distributionId) {
// 先存储 // 先存储
this.Cookies.setItem("distributionId", params.distributionId); storage.setItem("distributionId", params.distributionId);
let _this = this;
// 绑定关系 // 绑定关系
getGoodsDistribution(params.distributionId).then((res) => { getGoodsDistribution(params.distributionId).then((res) => {
// 绑定成功,则清除关系 // 绑定成功,则清除关系
if (res.success) { if (res.success) {
_this.Cookies.removeItem("distributionId"); storage.removeItem("distributionId");
} }
}); });
} }
goodsSkuDetail(params) goodsSkuDetail(params)
.then((res) => { .then((res) => {
this.isLoading = false; isLoading.value = false;
if (res.success) { if (res.success) {
const result = res.result; const result = res.result;
@@ -106,27 +109,28 @@ export default {
name: cateName ? cateName[index] : "", name: cateName ? cateName[index] : "",
}); });
}); });
this.categoryBar = cateArr; categoryBar.value = cateArr;
this["goodsMsg"] = res.result; goodsMsg.value = res.result;
if (!this.goodsMsg.data.intro) { if (!goodsMsg.value.data.intro) {
this.goodsMsg.data.intro = '' goodsMsg.value.data.intro = ''
} }
} else { } else {
Message.error(res.message); Message.error(res.message);
this.isLoading = false isLoading.value = false
} }
}) })
.catch((e) => { .catch((e) => {
this.isLoading = false isLoading.value = false
if(e.code === 11001){ if(e.code === 11001){
this.takeDownSale = true takeDownSale.value = true
} }
}); });
}, }
goGoodsList (currIndex) {
function goGoodsList (currIndex) {
// 跳转商品列表 // 跳转商品列表
const arr = []; const arr = [];
this.categoryBar.forEach((e, index) => { categoryBar.value.forEach((e, index) => {
if (index <= currIndex) { if (index <= currIndex) {
arr.push(e.id); arr.push(e.id);
} }
@@ -135,14 +139,10 @@ export default {
path: "/goodsList", path: "/goodsList",
query: { categoryId: arr.toString() }, query: { categoryId: arr.toString() },
}; };
}, }
},
watch: {}, // 初始化(原 created 钩子逻辑)
components: { Search, getGoodsDetail();
ShowGoods,
ShowGoodsDetail,
empty },
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.goods-detail-page { .goods-detail-page {

View File

@@ -104,103 +104,113 @@
</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 { </script>
sortIndex: 0, // 排序状态
priceSortIndex: 4, <script setup>
sortPriceIndex: false, // 判断价格升序还是降序 import { computed, reactive, ref, watch } from "vue";
searched: false, import { useRoute, useRouter } from "vue-router";
recommendList: [], import { Message } from "@/utils/message";
goodsTool: [ 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: "" },
{ title: "销量", en: "buyCount" }, { title: "销量", en: "buyCount" },
{ title: "评论数", en: "commentNum" }, { title: "评论数", en: "commentNum" },
{ title: "新品", en: "releaseTime" }, { title: "新品", en: "releaseTime" },
], ];
goodsList: [], // 商品列表 const goodsList = ref([]); // 商品列表
loading: false, // 加载状态 const loading = ref(false); // 加载状态
total: 0, // 列表总数 const total = ref(0); // 列表总数
params: { // 请求参数
// 请求参数 const params = reactive({
pageNumber: 1, pageNumber: 1,
pageSize: 20, pageSize: 20,
categoryId: "", categoryId: "",
}, });
};
}, const displayGoods = computed(() => {
watch: { if (goodsList.value.length) return goodsList.value;
$route() { return searched.value && !loading.value ? recommendList.value : [];
const { keyword, categoryId, promotionType, promotionsId } = this.$route.query; });
const emptyTitle = computed(() => {
const keyword = params.keyword;
return keyword ? `没有找到与“${keyword}”相关的商品` : "暂无商品";
});
watch(
() => route.query,
(query) => {
const { keyword, categoryId, promotionType, promotionsId } = query;
if (categoryId) { if (categoryId) {
const cateId = categoryId.split(","); const cateId = categoryId.split(",");
Object.assign(this.params, this.$route.query); Object.assign(params, query);
this.params.categoryId = cateId[cateId.length - 1]; params.categoryId = cateId[cateId.length - 1];
} }
if (promotionType) { if (promotionType) {
this.params.promotionType = promotionType; params.promotionType = promotionType;
} }
if (promotionsId) { if (promotionsId) {
this.params.promotionsId = promotionsId; params.promotionsId = promotionsId;
} }
if (keyword) { if (keyword) {
// handleSearch 内部会调 getGoodsList,此处不再重复请求 // handleSearch 内部会调 getGoodsList,此处不再重复请求
this.handleSearch(keyword); handleSearch(keyword);
} else { } else {
this.params.keyword = ""; params.keyword = "";
this.getGoodsList(); getGoodsList();
} }
}, }
}, );
computed: {
displayGoods() { // 搜索
if (this.goodsList.length) return this.goodsList; function handleSearch(key) {
return this.searched && !this.loading ? this.recommendList : []; params.keyword = key;
}, route.query.keyword = key;
emptyTitle() { params.pageNumber = 1;
const keyword = this.params.keyword; getGoodsList();
return keyword ? `没有找到与“${keyword}”相关的商品` : "暂无商品"; }
},
}, // 排序
methods: { function orderBy(data, index) {
// 搜索 sortIndex.value = index;
handleSearch(key) { params.sort = data;
this.params.keyword = key; params.order = "desc";
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 (data === "price") {
if (!this.sortPriceIndex) { if (!sortPriceIndex.value) {
this.sortPriceIndex = "asc"; sortPriceIndex.value = "asc";
} else { } else {
this.sortPriceIndex === "desc" sortPriceIndex.value === "desc"
? (this.sortPriceIndex = "asc") ? (sortPriceIndex.value = "asc")
: (this.sortPriceIndex = "desc"); : (sortPriceIndex.value = "desc");
} }
this.params.order = this.sortPriceIndex; params.order = sortPriceIndex.value;
} else { } else {
this.sortPriceIndex = false; sortPriceIndex.value = false;
} }
this.getGoodsList(); getGoodsList();
}, }
formatGrade(grade) {
function formatGrade(grade) {
if (grade === null || grade === undefined || grade === "") { if (grade === null || grade === undefined || grade === "") {
return 100; return 100;
} }
@@ -210,91 +220,89 @@ export default {
} }
const fixed = Number(num.toFixed(1)); const fixed = Number(num.toFixed(1));
return fixed % 1 === 0 ? fixed.toFixed(0) : String(fixed); return fixed % 1 === 0 ? fixed.toFixed(0) : String(fixed);
}, }
goGoodsDetail(skuId, goodsId) {
function goGoodsDetail(skuId, goodsId) {
const location = { const location = {
path: "/goodsDetail", path: "/goodsDetail",
query: { skuId, goodsId }, query: { skuId, goodsId },
}; };
window.open(this.$router.resolve(location).href, "_blank"); window.open(router.resolve(location).href, "_blank");
}, }
goShopPage(id) {
function goShopPage(id) {
if (!id) { if (!id) {
Message.warning("店铺信息不存在"); Message.warning("店铺信息不存在");
return; return;
} }
this.$router.push({ router.push({
path: "/merchant", path: "/merchant",
query: { id }, query: { id },
}); });
}, }
// 分页 修改页码
changePageNum(val) {
this.params.pageNumber = val;
this.getGoodsList();
},
// 分页 修改页数
changePageSize(val) {
this.params.pageNumber = 1;
this.params.pageSize = val;
this.getGoodsList();
},
// 获取商品列表 // 分页 修改页码
getGoodsList() { function changePageNum(val) {
this.loading = true; params.pageNumber = val;
getGoodsList();
}
// 分页 修改页数
function changePageSize(val) {
params.pageNumber = 1;
params.pageSize = val;
getGoodsList();
}
// 获取商品列表
function getGoodsList() {
loading.value = true;
apiGoods apiGoods
.goodsList(this.params) .goodsList(params)
.then((res) => { .then((res) => {
this.loading = false; loading.value = false;
if (res.success) { if (res.success) {
this.goodsList = res.result.records || []; goodsList.value = res.result.records || [];
this.total = res.result.total; total.value = res.result.total;
if (!this.goodsList.length) this.loadRecommend(); if (!goodsList.value.length) loadRecommend();
} }
}) })
.catch(() => { .catch(() => {
this.goodsList = []; goodsList.value = [];
}) })
.finally(() => { .finally(() => {
this.loading = false; loading.value = false;
this.searched = true; searched.value = true;
}); });
}, }
loadRecommend() {
if (this.recommendList.length) return; function loadRecommend() {
if (recommendList.value.length) return;
apiGoods apiGoods
.goodsList({ pageNumber: 1, pageSize: 10, sort: "buyCount", order: "desc" }) .goodsList({ pageNumber: 1, pageSize: 10, sort: "buyCount", order: "desc" })
.then((res) => { .then((res) => {
if (res.success) this.recommendList = res.result.records || []; if (res.success) recommendList.value = res.result.records || [];
}) })
.catch(() => { .catch(() => {
// 推荐列表为可选数据,加载失败不阻塞搜索页 // 推荐列表为可选数据,加载失败不阻塞搜索页
}); });
}, }
getParams(val) {
// 筛选条件回显 // 筛选条件回显
Object.assign(this.params, val); function getParams(val) {
this.getGoodsList(); Object.assign(params, val);
}, getGoodsList();
}, }
created() {
if (this.$route.query.categoryId) { // 初始化(原 created 钩子逻辑)
let cateId = this.$route.query.categoryId.split(","); if (route.query.categoryId) {
Object.assign(this.params, this.$route.query); const cateId = route.query.categoryId.split(",");
this.params.categoryId = cateId[cateId.length - 1]; Object.assign(params, route.query);
} else { params.categoryId = cateId[cateId.length - 1];
Object.assign(this.params, this.$route.query); } else {
} Object.assign(params, route.query);
this.getGoodsList(); }
}, getGoodsList();
components: {
GoodsClassNav,
GoodsThumb,
ArrowDown,
ArrowUp,
},
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -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,96 +41,78 @@
</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: {
type: null,
default: "",
},
});
const autoCoupList = ref([]);
const showCpmodel = ref(false);
const modelFormData = ref({ list: [] }); // 楼层装修数据
const topAdvert = ref({}); // 顶部广告
const showNav = ref(false); // 是否展示分类栏
const topSearchShow = ref(false); // 滚动后顶部搜索栏展示
const carouselLarge = ref(false); // 不同轮播分类尺寸
const carouselOpacity = ref(false); // 不同轮播分类样式
// 优惠券可用范围
function useScope(type, storeName) {
let shop = '平台'; let shop = '平台';
let goods = '全部商品' let goods = '全部商品';
if (storeName !== 'platform') shop = storeName if (storeName !== 'platform') shop = storeName;
switch (type) { switch (type) {
case 'ALL': case 'ALL':
goods = '全部商品' goods = '全部商品';
break; break;
case 'PORTION_GOODS': case 'PORTION_GOODS':
goods = '部分商品' goods = '部分商品';
break; break;
case 'PORTION_GOODS_CATEGORY': case 'PORTION_GOODS_CATEGORY':
goods = '部分分类商品' goods = '部分分类商品';
break; break;
} }
return `${shop}${goods}可用` return `${shop}${goods}可用`;
}, }
getAutoCoup(){
let data = new Date() function fetchAutoCoup() {
let datas = data.getDate() let data = new Date();
let hours = data.getHours() let datas = data.getDate();
let flagCoup = storage.getItem('getTimes') //缓存 let hours = data.getHours();
if(flagCoup && flagCoup != undefined && flagCoup != null){ //判断当前是否有缓存 let flagCoup = storage.getItem('getTimes'); //缓存
if(Number(datas) > Number(flagCoup)){ //判断缓存是否小于当前天数 if (flagCoup && flagCoup != undefined && flagCoup != null) { //判断当前是否有缓存
if(Number(hours) >= 6){//超过或等于6时清楚缓存 if (Number(datas) > Number(flagCoup)) { //判断缓存是否小于当前天数
storage.setItem('getTimes',datas)//存储缓存 if (Number(hours) >= 6) { //超过或等于6时清楚缓存
this.getcps() storage.setItem('getTimes', datas); //存储缓存
getcps();
} }
} }
}else{ } else {
// window.localStorage.setItem('getTimes',datas)//存储缓存 getcps();
this.getcps()
} }
}, }
getcps(){
let data = new Date() function getcps() {
let datas = data.getDate() let data = new Date();
getAutoCoup().then(res=>{ //调用自动发券 let datas = data.getDate();
if(res.success){ getAutoCoup().then(res => { //调用自动发券
this.autoCoupList.push(...res.result); if (res.success) {
autoCoupList.value.push(...res.result);
let objs = {}; let objs = {};
this.autoCoupList = this.autoCoupList.reduce((cur, next) => { autoCoupList.value = autoCoupList.value.reduce((cur, next) => {
//对象去重 //对象去重
if (next.id != undefined) { if (next.id != undefined) {
objs[next.id] objs[next.id]
@@ -139,30 +121,30 @@ export default {
} }
return cur; return cur;
}, []); }, []);
if(this.autoCoupList != '' && this.autoCoupList.length > 0){ if (autoCoupList.value != '' && autoCoupList.value.length > 0) {
this.showCpmodel = true; showCpmodel.value = true;
} }
storage.setItem('getTimes',datas)//存储缓存 storage.setItem('getTimes', datas); //存储缓存
} }
}).catch((err) => { }).catch((err) => {
console.warn('获取自动发券信息失败', 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 {
else{
// 获取首页装修数据 // 获取首页装修数据
indexData({ clientType: 'PC' }).then(async (res) => { indexData({ clientType: 'PC' }).then(async (res) => {
if (res.success && res.result?.pageData) { if (res.success && res.result?.pageData) {
try { try {
await this.parsePageData(res.result.pageData); await parsePageData(res.result.pageData);
} catch { } catch {
// 装修数据解析失败时保持默认空楼层 // 装修数据解析失败时保持默认空楼层
} }
@@ -171,9 +153,9 @@ export default {
console.warn('获取首页装修数据失败', err); console.warn('获取首页装修数据失败', err);
}); });
} }
}, }
async parsePageData(pageData){ async function parsePageData(pageData) {
const dataJson = typeof pageData === "string" ? JSON.parse(pageData) : pageData; const dataJson = typeof pageData === "string" ? JSON.parse(pageData) : pageData;
if (!dataJson?.list?.length) { if (!dataJson?.list?.length) {
return; return;
@@ -181,44 +163,57 @@ export default {
// 秒杀活动不是装修的数据,需要调用接口判断是否有秒杀商品 // 秒杀活动不是装修的数据,需要调用接口判断是否有秒杀商品
// 轮播图根据不同轮播,样式不同 // 轮播图根据不同轮播,样式不同
for (let i = 0; i < dataJson.list.length; i++) { for (let i = 0; i < dataJson.list.length; i++) {
let type = dataJson.list[i].type let type = dataJson.list[i].type;
if (type === 'carousel2') { if (type === 'carousel2') {
this.carouselLarge = true; carouselLarge.value = true;
} else if (type === 'carousel1') { } else if (type === 'carousel1') {
this.carouselLarge = true carouselLarge.value = true;
this.carouselOpacity = true carouselOpacity.value = true;
} else if (type === 'seckill') { } else if (type === 'seckill') {
let seckill = await this.getListByDay() let seckill = await getListByDay();
dataJson.list[i].options.list = seckill dataJson.list[i].options.list = seckill;
} }
} }
this.modelForm = dataJson; modelFormData.value = dataJson;
if (dataJson.list[1]) { if (dataJson.list[1]) {
storage.setItem('navList', dataJson.list[1]) storage.setItem('navList', dataJson.list[1]);
} }
this.showNav = true showNav.value = true;
if (dataJson.list[0]) { if (dataJson.list[0]) {
this.topAdvert = dataJson.list[0]; topAdvert.value = dataJson.list[0];
} }
}, }
async getListByDay () { // 当天秒杀活动 async function getListByDay() { // 当天秒杀活动
try { try {
const res = await seckillByDay() const res = await seckillByDay();
if (res.success && res.result?.length) { if (res.success && res.result?.length) {
return res.result return res.result;
} }
} catch { } catch {
// 秒杀接口失败时不影响首页其他模块 // 秒杀接口失败时不影响首页其他模块
} }
return [] 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();
} }
}, });
components: { Search,
ModelForm, onUnmounted(() => {
HoverSearch, if (window.onscroll === handleScroll) {
fixedBar } window.onscroll = null;
}; }
});
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -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,33 +119,38 @@ 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: { const route = useRoute();
verify, const router = useRouter();
User, const appStore = useAppStore();
Lock,
Document, const logoImg = computed(() => appStore.logoImg);
Iphone,
}, const formInlineRef = ref(null);
data() { const formSmsRef = ref(null);
return { const verifyRef = ref(null);
config, const formRefs = {
type: true, // true 账号登录 false 验证码登录 formInline: formInlineRef,
formData: { formSms: formSmsRef,
};
const type = ref(true); // true 账号登录 false 验证码登录
const formData = reactive({
// 登录表单 // 登录表单
username: "", username: "",
password: "", password: "",
}, });
formSms: { const formSms = reactive({
// 手机号登录 // 手机号登录
code: "", code: "",
mobile: "", mobile: "",
}, });
verifyStatus: false, // 是否图片验证通过 const verifyStatus = ref(false); // 是否图片验证通过
codeSent: false, // 是否已成功发送短信验证码 const codeSent = ref(false); // 是否已成功发送短信验证码
ruleInline: { const ruleInline = {
// 验证规则 // 验证规则
username: [{required: true, message: "请输入用户名"}], username: [{required: true, message: "请输入用户名"}],
password: [ password: [
@@ -158,32 +165,30 @@ export default {
}, },
], ],
code: [{required: true, message: "请输入手机验证码"}], code: [{required: true, message: "请输入手机验证码"}],
}, };
codeMsg: "发送验证码", // 验证码文字 const codeMsg = ref("发送验证码"); // 验证码文字
interval: null, // 定时器 const interval = ref(null); // 定时器
time: 60, // 倒计时 const time = ref(60); // 倒计时
year: new Date().getFullYear(), const year = new Date().getFullYear();
};
},
methods: {
hideVerify() {
this.$refs.verify?.close?.();
},
// 登录 function hideVerify() {
handleSubmit(name) { verifyRef.value?.close?.();
this.$refs[name].validate((valid) => { }
// 登录
function handleSubmit(name) {
formRefs[name].value.validate((valid) => {
if (valid) { if (valid) {
if (this.type) { if (type.value) {
this.$refs.verify?.init(); verifyRef.value?.init();
} else { } else {
if (!this.codeSent) { if (!codeSent.value) {
Message.warning("请先获取短信验证码"); Message.warning("请先获取短信验证码");
return; return;
} }
const data = JSON.parse(JSON.stringify(this.formSms)); const data = JSON.parse(JSON.stringify(formSms));
apiLogin.smsLogin(data).then((res) => { apiLogin.smsLogin(data).then((res) => {
this.hideVerify(); hideVerify();
if (res.success) { if (res.success) {
Message.success("登录成功"); Message.success("登录成功");
storage.setItem("accessToken", res.result.accessToken); storage.setItem("accessToken", res.result.accessToken);
@@ -192,14 +197,14 @@ export default {
apiLogin.getMemberMsg().then((res) => { apiLogin.getMemberMsg().then((res) => {
if (res.success) { if (res.success) {
storage.setItem("userInfo", res.result); storage.setItem("userInfo", res.result);
let query = this.$route.query; let query = route.query;
if (query.rePath) { if (query.rePath) {
this.$router.push({ router.push({
path: query.rePath, path: query.rePath,
query: JSON.parse(query.query), query: JSON.parse(query.query),
}); });
} else { } else {
this.$router.push("/"); router.push("/");
} }
} }
}); });
@@ -212,63 +217,63 @@ export default {
} }
} }
}); });
}, }
// 发送手机验证码 // 发送手机验证码
sendCode() { function sendCode() {
if (this.time !== 60) return; if (time.value !== 60) return;
if (this.formSms.mobile === "") { if (formSms.mobile === "") {
Message.warning("请先填写手机号"); Message.warning("请先填写手机号");
return; return;
} }
if (!RegExp.mobile.test(this.formSms.mobile)) { if (!RegExp.mobile.test(formSms.mobile)) {
Message.warning("请输入正确的手机号"); Message.warning("请输入正确的手机号");
return; return;
} }
this.$refs.verify?.init(); verifyRef.value?.init();
}, }
dispatchSms() { function dispatchSms() {
sendSms({ sendSms({
mobile: this.formSms.mobile, mobile: formSms.mobile,
verificationEnums: "LOGIN", verificationEnums: "LOGIN",
}).then((res) => { }).then((res) => {
if (res.success) { if (res.success) {
Message.success("验证码发送成功"); Message.success("验证码发送成功");
this.codeSent = true; codeSent.value = true;
this.interval = setInterval(() => { interval.value = setInterval(() => {
this.time--; time.value--;
if (this.time === 0) { if (time.value === 0) {
this.time = 60; time.value = 60;
this.codeMsg = "重新发送"; codeMsg.value = "重新发送";
this.verifyStatus = false; verifyStatus.value = false;
this.codeSent = false; codeSent.value = false;
clearInterval(this.interval); clearInterval(interval.value);
} else { } else {
this.codeMsg = this.time; codeMsg.value = time.value;
} }
}, 1000); }, 1000);
} else { } else {
this.verifyStatus = false; verifyStatus.value = false;
Message.warning(res.message); Message.warning(res.message);
} }
}).catch(() => { }).catch(() => {
this.verifyStatus = false; verifyStatus.value = false;
}); });
}, }
verifyChange(con) { function verifyChange(con) {
// 拼图验证码回显 // 拼图验证码回显
if (!con.status) return; if (!con.status) return;
if (this.type === true) { if (type.value === true) {
// 账号密码登录 // 账号密码登录
let data = JSON.parse(JSON.stringify(this.formData)); let data = JSON.parse(JSON.stringify(formData));
data.password = md5(data.password); data.password = md5(data.password);
this.hideVerify(); hideVerify();
Spin.show(); Spin.show();
apiLogin apiLogin
.login(data) .login(data)
.then((res) => { .then((res) => {
if (res.success) { if (res.success) {
this.loginSuccess(res.result.accessToken,res.result.refreshToken); loginSuccess(res.result.accessToken,res.result.refreshToken);
} else { } else {
Spin.hide(); Spin.hide();
Message.error(res.message); Message.error(res.message);
@@ -278,16 +283,16 @@ export default {
Spin.hide(); Spin.hide();
}); });
} else { } else {
this.verifyStatus = true; verifyStatus.value = true;
this.hideVerify(); hideVerify();
this.dispatchSms(); dispatchSms();
} }
}, }
handleWebLogin(type) { function handleWebLogin(type) {
// 第三方登录 // 第三方登录
webLogin(type); webLogin(type);
}, }
loginSuccess(accessToken,refreshToken){ function loginSuccess(accessToken,refreshToken){
Message.success("登录成功"); Message.success("登录成功");
storage.setItem("accessToken", accessToken); storage.setItem("accessToken", accessToken);
storage.setItem("refreshToken", refreshToken); storage.setItem("refreshToken", refreshToken);
@@ -295,57 +300,59 @@ export default {
Spin.hide(); Spin.hide();
if (res.success) { if (res.success) {
storage.setItem("userInfo", res.result); storage.setItem("userInfo", res.result);
let query = this.$route.query; let query = route.query;
if (query.rePath) { if (query.rePath) {
this.$router.push({ router.push({
path: query.rePath, path: query.rePath,
query: JSON.parse(query.query), query: JSON.parse(query.query),
}); });
} else { } else {
this.$router.push("/"); router.push("/");
} }
} }
}); });
}, }
}, onBeforeUnmount(() => {
beforeUnmount() { clearInterval(interval.value);
clearInterval(this.interval); });
},
mounted() { onMounted(() => {
let uuid = this.$route.query.state; let uuid = route.query.state;
if (uuid) { if (uuid) {
storage.setItem("uuid", uuid); storage.setItem("uuid", uuid);
loginCallback(uuid).then((res) => { loginCallback(uuid).then((res) => {
if (res.success) { if (res.success) {
this.loginSuccess(res.result.accessToken,res.result.refreshToken); loginSuccess(res.result.accessToken,res.result.refreshToken);
} }
}); });
} }
}, });
watch: {
type(v) { watch(type, (v) => {
if (v) { if (v) {
this.$refs.formInline.resetFields(); formInlineRef.value.resetFields();
} else { } else {
this.$refs.formSms.resetFields(); formSmsRef.value.resetFields();
} }
this.verifyStatus = false; verifyStatus.value = false;
this.codeSent = false; codeSent.value = false;
this.hideVerify(); hideVerify();
clearInterval(this.interval); clearInterval(interval.value);
this.codeMsg = "发送验证码"; codeMsg.value = "发送验证码";
this.time = 60; time.value = 60;
}, });
"formSms.mobile"() {
this.codeSent = false; watch(
this.verifyStatus = false; () => formSms.mobile,
clearInterval(this.interval); () => {
this.codeMsg = "发送验证码"; codeSent.value = false;
this.time = 60; verifyStatus.value = false;
}, clearInterval(interval.value);
}, codeMsg.value = "发送验证码";
}; time.value = 60;
}
);
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.login { .login {

View File

@@ -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,130 +132,127 @@ 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 =
document.documentElement.scrollTop || document.body.scrollTop;
this.topSearchShow = top > 300;
};
},
methods: {
isFloorEnabled() {
const pageShow = this.storeMsg && this.storeMsg.pageShow;
return pageShow === "1" || pageShow === 1 || pageShow === "OPEN" || pageShow === true; return pageShow === "1" || pageShow === 1 || pageShow === "OPEN" || pageShow === true;
}, }
loadStoreGoodsView() {
this.showFloorView = false; function loadStoreGoodsView() {
this.getGoodsList(); showFloorView.value = false;
this.getCateList(); getGoodsList();
}, getCateList();
getIndexData() { }
function getIndexData() {
getFloorStoreData({ getFloorStoreData({
clientType: "PC", clientType: "PC",
num: this.$route.query.id, num: route.query.id,
pageType: "STORE", pageType: "STORE",
}) })
.then(async (res) => { .then(async (res) => {
if (res.success && res.result?.pageData) { if (res.success && res.result?.pageData) {
try { try {
await this.parsePageData(res.result.pageData); await parsePageData(res.result.pageData);
this.showFloorView = true; showFloorView.value = true;
} catch { } catch {
this.loadStoreGoodsView(); loadStoreGoodsView();
} }
} else { } else {
this.loadStoreGoodsView(); loadStoreGoodsView();
} }
}) })
.catch(() => { .catch(() => {
this.loadStoreGoodsView(); loadStoreGoodsView();
}); });
}, }
async parsePageData(pageData) {
async function parsePageData(pageData) {
const dataJson = JSON.parse(pageData); const dataJson = JSON.parse(pageData);
for (let i = 0; i < dataJson.list.length; i++) { for (let i = 0; i < dataJson.list.length; i++) {
const type = dataJson.list[i].type; const type = dataJson.list[i].type;
if (type === "carousel2") { if (type === "carousel2") {
this.carouselLarge = true; carouselLarge.value = true;
} else if (type === "carousel1") { } else if (type === "carousel1") {
this.carouselLarge = true; carouselLarge.value = true;
this.carouselOpacity = true; carouselOpacity.value = true;
} else if (type === "seckill") { } else if (type === "seckill") {
dataJson.list[i].options.list = await this.getListByDay(); dataJson.list[i].options.list = await getListByDay();
} }
} }
this.modelForm = dataJson; modelForm.value = dataJson;
storage.setItem("navList", dataJson.list[1]); storage.setItem("navList", dataJson.list[1]);
this.showNav = true; showNav.value = true;
this.topAdvert = dataJson.list[0]; topAdvert.value = dataJson.list[0];
}, }
async getListByDay() {
async function getListByDay() {
const res = await seckillByDay(); const res = await seckillByDay();
if (res.success && res.result?.length) { if (res.success && res.result?.length) {
return res.result; return res.result;
} }
return []; return [];
}, }
function checkStoreCollectionStatus() {
checkStoreCollectionStatus() { const storeId = storeMsg.value && storeMsg.value.storeId;
const storeId = this.storeMsg && this.storeMsg.storeId; if (!storeId || !storage.getItem("userInfo")) {
if (!storeId || !this.Cookies.getItem("userInfo")) {
return; return;
} }
isStoreCollection("STORE", storeId) isStoreCollection("STORE", storeId)
.then((res) => { .then((res) => {
if (res.success && res.result) { if (res.success && res.result) {
this.storeCollected = true; storeCollected.value = true;
} }
}) })
.catch(() => { .catch(() => {
// 收藏状态查询失败不影响店铺页展示,静默处理 // 收藏状态查询失败不影响店铺页展示,静默处理
}); });
}, }
getStoreMsg() {
const storeId = this.$route.query.id; function getStoreMsg() {
const storeId = route.query.id;
if (!storeId) { if (!storeId) {
Message.warning("店铺不存在"); Message.warning("店铺不存在");
return; return;
@@ -261,110 +260,120 @@ export default {
getDetailById(storeId) getDetailById(storeId)
.then((res) => { .then((res) => {
if (res.success && res.result) { if (res.success && res.result) {
this.storeMsg = res.result; storeMsg.value = res.result;
document.title = this.storeMsg.storeName || "店铺"; document.title = storeMsg.value.storeName || "店铺";
this.checkStoreCollectionStatus(); checkStoreCollectionStatus();
if (this.isFloorEnabled()) { if (isFloorEnabled()) {
this.getIndexData(); getIndexData();
} else { } else {
this.loadStoreGoodsView(); loadStoreGoodsView();
} }
} else { } else {
this.storeMsg = { storeId }; storeMsg.value = { storeId };
this.loadStoreGoodsView(); loadStoreGoodsView();
} }
}) })
.catch(() => { .catch(() => {
this.storeMsg = { storeId }; storeMsg.value = { storeId };
this.loadStoreGoodsView(); loadStoreGoodsView();
}); });
}, }
getCateList() {
function getCateList() {
// 店铺分类 // 店铺分类
getCateById(this.$route.query.id).then((res) => { getCateById(route.query.id).then((res) => {
if (res.success) { if (res.success) {
this.cateList = res.result; cateList.value = res.result;
} }
}); });
}, }
getGoodsList() {
function getGoodsList() {
// 商品信息 // 商品信息
goodsList(this.params) goodsListApi(params)
.then((res) => { .then((res) => {
if (res.success) { if (res.success) {
this.goodsList = res.result.records; goodsList.value = res.result.records;
this.total = res.result.total; total.value = res.result.total;
} }
}) })
.catch((err) => { .catch((err) => {
console.warn('获取商品列表失败', err); console.warn('获取商品列表失败', err);
}); });
}, }
goGoodsDetail(skuId, goodsId) {
function goGoodsDetail(skuId, goodsId) {
// 跳转商品详情 // 跳转商品详情
let routeUrl = this.$router.resolve({ let routeUrl = router.resolve({
path: "/goodsDetail", path: "/goodsDetail",
query: {skuId, goodsId}, query: {skuId, goodsId},
}); });
window.open(routeUrl.href, "_blank"); window.open(routeUrl.href, "_blank");
}, }
search(val) {
function search(val) {
// 搜索本店商品 // 搜索本店商品
this.params.keyword = val; params.keyword = val;
this.getGoodsList(); getGoodsList();
}, }
searchByCate(cate) {
function searchByCate(cate) {
// 搜索同分类下商品 // 搜索同分类下商品
this.params.storeCatId = cate.id; params.storeCatId = cate.id;
this.params.pageNumber = 1; params.pageNumber = 1;
this.cateName = cate.labelName; cateName.value = cate.labelName;
this.getGoodsList(); getGoodsList();
}, }
isCateActive(cate) {
if (!cate || !this.params.storeCatId) return false; function isCateActive(cate) {
if (this.params.storeCatId === cate.id) return true; if (!cate || !params.storeCatId) return false;
return Array.isArray(cate.children) && cate.children.some((sec) => sec.id === this.params.storeCatId); if (params.storeCatId === cate.id) return true;
}, return Array.isArray(cate.children) && cate.children.some((sec) => sec.id === params.storeCatId);
formatGrade(grade) { }
function formatGrade(grade) {
if (grade === null || grade === undefined || grade === "") return 100; if (grade === null || grade === undefined || grade === "") return 100;
const num = Number(grade); const num = Number(grade);
if (Number.isNaN(num)) return 100; if (Number.isNaN(num)) return 100;
const fixed = Number(num.toFixed(1)); const fixed = Number(num.toFixed(1));
return fixed % 1 === 0 ? fixed.toFixed(0) : String(fixed); return fixed % 1 === 0 ? fixed.toFixed(0) : String(fixed);
}, }
// 分页 修改页码
changePageNum(val) { // 分页 修改页码
this.params.pageNumber = val; function changePageNum(val) {
this.getGoodsList(); params.pageNumber = val;
}, getGoodsList();
// 分页 修改页数 }
changePageSize(val) {
this.params.pageNumber = 1; // 分页 修改页数
this.params.pageSize = val; function changePageSize(val) {
this.getGoodsList(); params.pageNumber = 1;
}, params.pageSize = val;
async collect() { getGoodsList();
}
async function collect() {
// 收藏店铺 // 收藏店铺
const storeId = this.storeMsg && this.storeMsg.storeId; const storeId = storeMsg.value && storeMsg.value.storeId;
if (!storeId) { if (!storeId) {
return; return;
} }
if (!this.Cookies.getItem("userInfo")) { if (!storage.getItem("userInfo")) {
Message.warning("请先登录"); Message.warning("请先登录");
this.$router.push("/login"); router.push("/login");
return; return;
} }
try { try {
if (this.storeCollected) { if (storeCollected.value) {
const cancel = await cancelStoreCollect("STORE", storeId); const cancel = await cancelStoreCollect("STORE", storeId);
if (cancel.success) { if (cancel.success) {
Message.success("已取消收藏"); Message.success("已取消收藏");
this.storeCollected = false; storeCollected.value = false;
} }
} else { } else {
const collect = await collectStore("STORE", storeId); const collect = await collectStore("STORE", storeId);
if (collect.success || collect.code === 200) { if (collect.success || collect.code === 200) {
this.storeCollected = true; storeCollected.value = true;
Message.success( Message.success(
"收藏店铺成功,可以前往个人中心我的收藏查看" "收藏店铺成功,可以前往个人中心我的收藏查看"
); );
@@ -373,11 +382,75 @@ export default {
} catch (err) { } catch (err) {
const msg = err?.message || err?.data?.message || ""; const msg = err?.message || err?.data?.message || "";
if (msg.includes("重复收藏")) { if (msg.includes("重复收藏")) {
this.storeCollected = true; 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>

View File

@@ -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">

View File

@@ -45,21 +45,26 @@ export default {
window.scrollTo(0, 0); window.scrollTo(0, 0);
next(); next();
}, },
data () { };
return { </script>
detail: {} // 商品详情
}; <script setup>
}, import { onMounted, ref } from "vue";
mounted () { import { useRoute } from "vue-router";
const { skuId, goodsId, thumbnail, goodsName } = this.$route.query;
this.detail = { const route = useRoute();
const detail = ref({}); // 商品详情
onMounted(() => {
const { skuId, goodsId, thumbnail, goodsName } = route.query;
detail.value = {
skuId, skuId,
goodsId, goodsId,
thumbnail, thumbnail,
goodsName goodsName
}; };
} });
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -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,21 +102,23 @@ 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();
const formRegist = reactive({
// 注册表单 // 注册表单
mobilePhone: '', mobilePhone: '',
code: '', code: '',
username: '', username: '',
password: '' password: ''
}, });
ruleInline: { const ruleInline = reactive({
// 验证规则 // 验证规则
username: [{ required: true, message: '请输入用户名' }], username: [{ required: true, message: '请输入用户名' }],
password: [ password: [
@@ -131,30 +134,32 @@ export default {
} }
], ],
code: [{ required: true, message: '请输入手机验证码' }] code: [{ required: true, message: '请输入手机验证码' }]
}, });
verifyStatus: false, // 是否验证通过 const verifyStatus = ref(false); // 是否验证通过
verifyType: 'REGISTER', // 验证状态 const verifyType = ref('REGISTER'); // 验证状态
codeMsg: '发送验证码', // 提示文字 const codeMsg = ref('发送验证码'); // 提示文字
interval: '', // 定时器 const interval = ref(''); // 定时器
time: 60 // 倒计时 const time = ref(60); // 倒计时
};
}, const formRegistRef = ref(null);
methods: { const verify = ref(null);
hideVerify() {
this.$refs.verify?.close?.(); function hideVerify() {
}, verify.value?.close?.();
// 注册 }
handleRegist () {
this.$refs.formRegist.validate((valid) => { // 注册
function handleRegist () {
formRegistRef.value.validate((valid) => {
if (!valid) { if (!valid) {
return; return;
} }
const data = JSON.parse(JSON.stringify(this.formRegist)); const data = JSON.parse(JSON.stringify(formRegist));
data.password = md5(data.password); data.password = md5(data.password);
apiLogin.regist(data).then((res) => { apiLogin.regist(data).then((res) => {
if (res.success) { if (res.success) {
Message.success('注册成功!'); Message.success('注册成功!');
this.$router.push('login'); router.push('login');
} else { } else {
Message.warning(res.message || '注册失败'); Message.warning(res.message || '注册失败');
} }
@@ -164,57 +169,59 @@ export default {
}).catch(() => { }).catch(() => {
// 表单校验使用回调模式,此 catch 仅为兜底 // 表单校验使用回调模式,此 catch 仅为兜底
}); });
}, }
// 发送短信验证码
sendCode () { // 发送短信验证码
if (this.time !== 60) return; function sendCode () {
if (this.formRegist.mobilePhone === '') { if (time.value !== 60) return;
if (formRegist.mobilePhone === '') {
Message.warning('请先填写手机号'); Message.warning('请先填写手机号');
return; return;
} }
if (!RegExp.mobile.test(this.formRegist.mobilePhone)) { if (!RegExp.mobile.test(formRegist.mobilePhone)) {
Message.warning('请输入正确的手机号'); Message.warning('请输入正确的手机号');
return; return;
} }
this.$refs.verify?.init(); verify.value?.init();
}, }
dispatchSms () {
function dispatchSms () {
sendSms({ sendSms({
mobile: this.formRegist.mobilePhone, mobile: formRegist.mobilePhone,
verificationEnums: 'REGISTER' verificationEnums: 'REGISTER'
}).then(res => { }).then(res => {
if (res.success) { if (res.success) {
Message.success('验证码发送成功'); Message.success('验证码发送成功');
this.interval = setInterval(() => { interval.value = setInterval(() => {
this.time--; time.value--;
if (this.time === 0) { if (time.value === 0) {
this.time = 60; time.value = 60;
this.codeMsg = '重新发送'; codeMsg.value = '重新发送';
this.verifyStatus = false; verifyStatus.value = false;
clearInterval(this.interval); clearInterval(interval.value);
} else { } else {
this.codeMsg = this.time; codeMsg.value = time.value;
} }
}, 1000); }, 1000);
} else { } else {
this.verifyStatus = false; verifyStatus.value = false;
Message.warning(res.message || '验证码发送失败'); Message.warning(res.message || '验证码发送失败');
} }
}).catch(() => { }).catch(() => {
this.verifyStatus = false; verifyStatus.value = false;
}); });
}, }
verifyChange (con) {
function verifyChange (con) {
if (!con.status) return; if (!con.status) return;
this.hideVerify(); hideVerify();
this.verifyStatus = true; verifyStatus.value = true;
this.dispatchSms(); dispatchSms();
} }
},
mounted () { onMounted(() => {
this.$refs.formRegist.resetFields(); formRegistRef.value.resetFields();
} });
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.sign-up { .sign-up {

View File

@@ -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 },
mounted() {
// 接收id进行查询
const id = this.$route.query.id
this.init(id)
},
methods: {
async init(id) {
const res = await getTopicData(id) const res = await getTopicData(id)
if (res.success) { if (res.success) {
// 直接复用首页就行 // 直接复用首页就行
if (res.result.pageData) { if (res.result.pageData) {
this.pageData = JSON.parse(res.result.pageData) pageData.value = 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>

View File

@@ -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() { const route = useRoute();
return { const router = useRouter();
config,
detail: "", const configData = ref(config);
}; const detail = ref("");
},
mounted() { function back() {
this.init(); router.push("/");
}, }
methods: {
back() { // 初始化文章
this.$router.push("/"); function init() {
}, const id = route.query.id;
// 初始化文章
init() {
const id = this.$route.query.id;
articleDetail(id).then((res) => { articleDetail(id).then((res) => {
if (res.success) { if (res.success) {
this.detail = res.result; detail.value = res.result;
} }
}); });
}, }
},
}; onMounted(() => {
init();
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@@ -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) {
this.list = res.result || [] list.value = res.result || []
this.openName = [] openName.value = []
if (this.$route.query.id && this.detail && this.detail.categoryId) { if (route.query.id && detail.value && detail.value.categoryId) {
this.activeName = this.detail.categoryId activeName.value = detail.value.categoryId
this.list.forEach(e => { list.value.forEach(e => {
const children = e.children || [] const children = e.children || []
const hitParent = String(e.id) === String(this.activeName) const hitParent = String(e.id) === String(activeName.value)
const hitChild = children.some(i => i && String(i.id) === String(this.activeName)) const hitChild = children.some(i => i && String(i.id) === String(activeName.value))
if ((hitParent || hitChild) && children.length) { if ((hitParent || hitChild) && children.length) {
this.openName.push(String(e.id)) openName.value.push(String(e.id))
} }
}) })
} else if (this.list[0] && this.list[0].id) { } else if (list.value[0] && list.value[0].id) {
this.activeName = this.list[0].id activeName.value = list.value[0].id
if (this.list[0].children && this.list[0].children.length) { if (list.value[0].children && list.value[0].children.length) {
this.openName.push(String(this.list[0].id)) openName.value.push(String(list.value[0].id))
} }
} }
this.menuKey++ menuKey.value++
if (this.activeName) { if (activeName.value) {
this.getList(this.activeName) getList(activeName.value)
} }
} }
}) })
}, }
getList (id) {
this.params.categoryId = id function getList (id) {
articleList(this.params).then(res => { params.categoryId = id
fetchArticleList(params).then(res => {
if (res.success) { if (res.success) {
this.articleList = res.result.records articleList.value = res.result.records
} }
}) })
}, }
async getDetail (id) {
async function getDetail (id) {
await articleDetail(id).then(res => { await articleDetail(id).then(res => {
if (res.success) { if (res.success) {
this.detail = res.result detail.value = res.result
this.showList = false showList.value = false
} }
}) })
} }
},
async mounted () { onMounted(async () => {
const articleId = this.$route.query.id const articleId = route.query.id
if (articleId) { if (articleId) {
await this.getDetail(articleId) await getDetail(articleId)
} }
this.getCateList() getCateList()
} })
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -44,62 +44,53 @@
</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() {
return this.unreadMsg > 99 ? "99+" : String(this.unreadMsg);
},
orderStats() {
return [
{ key: "pay", label: "待付款", count: this.waitPay, action: () => this.goOrder("WAIT_PAY") },
{ 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") },
];
},
},
mounted() {
this.readUser();
this.loadSummary();
},
methods: {
readUser() {
try { try {
const raw = storage.getItem("userInfo"); const raw = storage.getItem("userInfo");
const user = raw ? JSON.parse(raw) : {}; const user = raw ? JSON.parse(raw) : {};
this.displayName = user.nickName || user.username || "会员"; displayName.value = user.nickName || user.username || "会员";
} catch { } catch {
this.displayName = "会员"; displayName.value = "会员";
} }
}, }
loadSummary() {
function loadSummary() {
const page = { pageNumber: 1, pageSize: 1 }; const page = { pageNumber: 1, pageSize: 1 };
Promise.all([ Promise.all([
getOrderList({ ...page, tag: "WAIT_PAY" }).catch(() => null), getOrderList({ ...page, tag: "WAIT_PAY" }).catch(() => null),
@@ -109,23 +100,28 @@ export default {
memberCouponList({ ...page, memberCouponStatus: "NEW" }).catch(() => null), memberCouponList({ ...page, memberCouponStatus: "NEW" }).catch(() => null),
getMembersWallet().catch(() => null), getMembersWallet().catch(() => null),
refreshUnreadMessage(), refreshUnreadMessage(),
]).then(([pay, ship, receive, afterSale, coupon, wallet]) => { ]).then(([pay, ship, receive, afterSaleRes, coupon, wallet]) => {
this.waitPay = totalOf(pay); waitPay.value = totalOf(pay);
this.waitShip = totalOf(ship); waitShip.value = totalOf(ship);
this.waitReceive = totalOf(receive); waitReceive.value = totalOf(receive);
this.afterSale = totalOf(afterSale); afterSale.value = totalOf(afterSaleRes);
this.couponCount = totalOf(coupon); couponCount.value = totalOf(coupon);
this.balance = wallet && wallet.success ? wallet.result.memberWallet || 0 : 0; balance.value = wallet && wallet.success ? wallet.result.memberWallet || 0 : 0;
}); });
}, }
go(name) {
this.$router.push({ name }); function go(name) {
}, router.push({ name });
goOrder(tag) { }
this.$router.push({ name: "MyOrder", query: { tag } });
}, function goOrder(tag) {
}, router.push({ name: "MyOrder", query: { tag } });
}; }
onMounted(() => {
readUser();
loadSummary();
});
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -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 }, defineOptions({ name: 'AccountSafe' });
name: 'AccountSafe',
data () { const router = useRouter();
return {
pwdStatus: '', const pwdStatus = ref('');
connectLoading: false, const connectLoading = ref(false);
bindModalVisible: false, const bindModalVisible = ref(false);
bindSubmitLoading: false, const bindSubmitLoading = ref(false);
connectBoundTypes: [], const connectBoundTypes = ref([]);
thirdAccountTypes: [ const thirdAccountTypes = ref([
{ type: 'WECHAT', label: '微信' } { type: 'WECHAT', label: '微信' }
], ]);
qrCodeLoading: false, const qrCodeLoading = ref(false);
qrCodeBase64: '', const qrCodeBase64 = ref('');
bindForm: { const bindForm = reactive({
type: '', type: '',
typeLabel: '', typeLabel: '',
unionID: '' unionID: ''
} });
}
}, const wechatConnect = computed(() => thirdAccountTypes.value[0]);
computed: { const qrCodeSrc = computed(() => {
wechatConnect () { if (!qrCodeBase64.value) return ''
return this.thirdAccountTypes[0] if (qrCodeBase64.value.startsWith('data:image')) return qrCodeBase64.value
}, return `data:image/png;base64,${qrCodeBase64.value}`
qrCodeSrc () { });
if (!this.qrCodeBase64) return '' const bindModalOkText = computed(() => {
if (this.qrCodeBase64.startsWith('data:image')) return this.qrCodeBase64 if (bindForm.type === 'WECHAT') return '我已完成绑定'
return `data:image/png;base64,${this.qrCodeBase64}`
},
bindModalOkText () {
if (this.bindForm.type === 'WECHAT') return '我已完成绑定'
return '确定' return '确定'
} });
},
mounted () { // 设置支付密码
this.getPwdStatus() function goModifyPwd () {
this.refreshConnectBinds() router.push({name: 'ModifyPwd', query: { status: 2 }})
}, }
methods: { function modifyPwd () { // 修改密码
// 设置支付密码 router.push({name: 'ModifyPwd', query: { status: 1 }})
goModifyPwd () { }
this.$router.push({name: 'ModifyPwd', query: { status: 2 }}) // 获取密码状态
}, function getPwdStatus () {}
modifyPwd () { // 修改密码 // function getPwdStatus () {
this.$router.push({name: 'ModifyPwd', query: { status: 1 }}) // getPwdStatus().then(res => {
}, // if (res) {
// 获取密码状态 // pwdStatus.value = '修改密码'
getPwdStatus () {}, // } else {
// getPwdStatus () { // pwdStatus.value = '设置密码'
// getPwdStatus().then(res => { // }
// if (res) { // });
// this.pwdStatus = '修改密码' // }
// } else { function isBound (type) {
// this.pwdStatus = '设置密码' return connectBoundTypes.value.includes(type)
// } }
// }); function refreshConnectBinds () {
// } connectLoading.value = true
isBound (type) {
return this.connectBoundTypes.includes(type)
},
refreshConnectBinds () {
this.connectLoading = true
return getThirdAccountBindList() return getThirdAccountBindList()
.then(res => { .then(res => {
if (res && res.success) { if (res && res.success) {
this.connectBoundTypes = Array.isArray(res.result) ? res.result : [] connectBoundTypes.value = Array.isArray(res.result) ? res.result : []
} }
}) })
.finally(() => { .finally(() => {
this.connectLoading = false connectLoading.value = false
}) })
}, }
openBindModal (item) { function openBindModal (item) {
this.bindForm.type = item.type bindForm.type = item.type
this.bindForm.typeLabel = item.label bindForm.typeLabel = item.label
this.bindForm.unionID = '' bindForm.unionID = ''
this.qrCodeBase64 = '' qrCodeBase64.value = ''
this.bindModalVisible = true bindModalVisible.value = true
if (item.type === 'WECHAT') { if (item.type === 'WECHAT') {
this.fetchWechatQrCode() fetchWechatQrCode()
} }
}, }
resetBindForm () { function resetBindForm () {
this.bindForm.type = '' bindForm.type = ''
this.bindForm.typeLabel = '' bindForm.typeLabel = ''
this.bindForm.unionID = '' bindForm.unionID = ''
this.qrCodeLoading = false qrCodeLoading.value = false
this.qrCodeBase64 = '' qrCodeBase64.value = ''
this.bindSubmitLoading = false bindSubmitLoading.value = false
}, }
fetchWechatQrCode () { function fetchWechatQrCode () {
if (this.bindForm.type !== 'WECHAT') return if (bindForm.type !== 'WECHAT') return
const rawUserInfo = storage.getItem('userInfo') const rawUserInfo = storage.getItem('userInfo')
const userInfo = rawUserInfo ? JSON.parse(rawUserInfo) : null const userInfo = rawUserInfo ? JSON.parse(rawUserInfo) : null
const userId = userInfo && userInfo.id ? String(userInfo.id) : '' const userId = userInfo && userInfo.id ? String(userInfo.id) : ''
if (!userId) { if (!userId) {
this.qrCodeBase64 = '' qrCodeBase64.value = ''
Message.error('未获取到当前用户ID,请重新登录后重试') Message.error('未获取到当前用户ID,请重新登录后重试')
return return
} }
this.qrCodeLoading = true qrCodeLoading.value = true
getWechatH5QrCode({ scene: userId }) getWechatH5QrCode({ scene: userId })
.then(res => { .then(res => {
if (res && res.success) { if (res && res.success) {
this.qrCodeBase64 = res.result || '' qrCodeBase64.value = res.result || ''
return return
} }
this.qrCodeBase64 = '' qrCodeBase64.value = ''
Message.error((res && res.message) || '二维码获取失败') Message.error((res && res.message) || '二维码获取失败')
}) })
.catch(() => { .catch(() => {
this.qrCodeBase64 = '' qrCodeBase64.value = ''
}) })
.finally(() => { .finally(() => {
this.qrCodeLoading = false qrCodeLoading.value = false
}) })
}, }
submitBind () { function submitBind () {
if (this.bindSubmitLoading) return if (bindSubmitLoading.value) return
if (this.bindForm.type === 'WECHAT' && !this.bindForm.unionID) { if (bindForm.type === 'WECHAT' && !bindForm.unionID) {
this.bindSubmitLoading = true bindSubmitLoading.value = true
this.refreshConnectBinds() refreshConnectBinds()
.then(() => { .then(() => {
if (this.isBound(this.bindForm.type)) { if (isBound(bindForm.type)) {
Message.success('绑定成功') Message.success('绑定成功')
this.bindModalVisible = false bindModalVisible.value = false
this.resetBindForm() resetBindForm()
return return
} }
Message.warning('暂未检测到绑定,请扫码后重试') Message.warning('暂未检测到绑定,请扫码后重试')
this.$nextTick(() => { nextTick(() => {
this.bindModalVisible = true bindModalVisible.value = true
}) })
}) })
.finally(() => { .finally(() => {
this.bindSubmitLoading = false bindSubmitLoading.value = false
}) })
return return
} }
if (!this.bindForm.type) { if (!bindForm.type) {
Message.error('绑定类型不能为空') Message.error('绑定类型不能为空')
return return
} }
if (!this.bindForm.unionID) { if (!bindForm.unionID) {
Message.error('OpenID/UnionID 不能为空') Message.error('OpenID/UnionID 不能为空')
this.$nextTick(() => { nextTick(() => {
this.bindModalVisible = true bindModalVisible.value = true
}) })
return return
} }
this.bindSubmitLoading = true bindSubmitLoading.value = true
bindThirdAccount({ unionID: this.bindForm.unionID, type: this.bindForm.type }) bindThirdAccount({ unionID: bindForm.unionID, type: bindForm.type })
.then(() => { .then(() => {
Message.success('绑定成功') Message.success('绑定成功')
this.bindModalVisible = false bindModalVisible.value = false
this.resetBindForm() resetBindForm()
this.refreshConnectBinds() refreshConnectBinds()
}) })
.finally(() => { .finally(() => {
this.bindSubmitLoading = false bindSubmitLoading.value = false
}) })
}, }
confirmUnbind (item) { function confirmUnbind (item) {
Modal.confirm({ Modal.confirm({
title: '解绑确认', title: '解绑确认',
content: `<p>确定解绑 ${item.label} 吗?</p>`, content: `<p>确定解绑 ${item.label} 吗?</p>`,
onOk: () => { onOk: () => {
unbindThirdAccount({ type: item.type }).then(() => { unbindThirdAccount({ type: item.type }).then(() => {
Message.success('解绑成功') Message.success('解绑成功')
this.refreshConnectBinds() refreshConnectBinds()
}) })
} }
}) })
}
}
} }
onMounted(() => {
getPwdStatus()
refreshConnectBinds()
});
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -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); // 加载状态
const list = ref([]); // 评价列表
const total = ref(0); // 评价总数
const params = reactive({ // 请求参数
pageNumber: 1, pageNumber: 1,
pageSize: 20 pageSize: 20
} });
};
}, function getList () { // 获取评价列表
mounted () { evolutionList(params).then(res => {
this.getList()
},
methods: {
getList () { // 获取评价列表
evolutionList(this.params).then(res => {
if (res.success) { if (res.success) {
const list = res.result.records; const records = res.result.records;
list.forEach(element => { records.forEach(element => {
element.descriptionScore = Number(element.descriptionScore) element.descriptionScore = Number(element.descriptionScore)
}); });
this.list = list; list.value = records;
this.total = res.result.total total.value = res.result.total
} }
}) })
}, }
changePageNum (val) { // 修改页码
this.params.pageNumber = val; function changePageNum (val) { // 修改页码
this.getList() params.pageNumber = val;
}, getList()
changePageSize (val) { // 修改页数 }
this.params.pageNumber = 1;
this.params.pageSize = val; function changePageSize (val) { // 修改页数
this.getList() params.pageNumber = 1;
}, params.pageSize = val;
evaluateDetail (id) { // 跳转评价详情 getList()
this.$router.push({path: '/home/evalDetail', query: { id }}) }
}
} function evaluateDetail (id) { // 跳转评价详情
}; router.push({path: '/home/evalDetail', query: { id }})
}
onMounted(() => {
getList()
});
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -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) { // 跳转商品详情 function goGoodsDetail (skuId, goodsId) { // 跳转商品详情
let routerUrl = this.$router.resolve({ let routerUrl = router.resolve({
path: '/goodsDetail', path: '/goodsDetail',
query: {skuId, goodsId} query: {skuId, goodsId}
}) })
window.open(routerUrl.href, '_blank') window.open(routerUrl.href, '_blank')
}, }
handleView (name) { // 预览图片 function handleView (name) { // 预览图片
this.previewImage = name; previewImage.value = name;
this.visible = true; visible.value = true;
}, }
// 回复消息 // 回复消息
handleSubmit () { function handleSubmit () {
if (this.params.content === '') { if (params.content === '') {
Message.error('请填写对话内容'); Message.error('请填写对话内容');
return; return;
} }
this.submitLoading = true; submitLoading.value = true;
this.params.complainId = this.$route.query.id; params.complainId = route.query.id;
communication(this.params).then((res) => { communication(params).then((res) => {
this.submitLoading = false; submitLoading.value = false;
if (res.success) { if (res.success) {
Message.success('对话成功'); Message.success('对话成功');
this.params.content = ''; params.content = '';
this.getDetail(); getDetail();
} }
}); });
}
},
mounted () {
this.getDetail()
}
} }
onMounted(() => {
getDetail()
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.speak-way { .speak-way {

View File

@@ -58,53 +58,53 @@
</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([]); // 投诉列表
const statusLabel = { // 投诉状态
NO_APPLY: '未申请', NO_APPLY: '未申请',
APPLYING: '申请中', APPLYING: '申请中',
COMPLETE: '已完成', COMPLETE: '已完成',
EXPIRED: '已失效', EXPIRED: '已失效',
CANCEL: '已取消', CANCEL: '已取消',
NEW: '新订单' NEW: '新订单'
}, };
total: 0, // 投诉总数 const total = ref(0); // 投诉总数
params: { // 请求参数 const params = reactive({ // 请求参数
pageNumber: 1, pageNumber: 1,
pageSize: 20 pageSize: 20
} });
};
}, function getList() { // 获取投诉列表
mounted () { complainList(params).then(res => {
this.getList()
},
methods: {
getList () { // 获取投诉列表
complainList(this.params).then(res => {
if (res.success) { if (res.success) {
const list = res.result.records; list.value = res.result.records;
this.list = list; total.value = res.result.total
this.total = res.result.total
} }
}) })
}, }
changePageNum (val) { // 改变页码
this.params.pageNumber = val; function changePageNum(val) { // 改变页码
this.getList() params.pageNumber = val;
}, getList()
changePageSize (val) { // 改变页数 }
this.params.pageNumber = 1;
this.params.pageSize = val; function changePageSize(val) { // 改变页数
this.getList() params.pageNumber = 1;
}, params.pageSize = val;
cancel (id) { // 取消投诉 getList()
}
function cancel(id) { // 取消投诉
Modal.confirm({ Modal.confirm({
title: '取消投诉', title: '取消投诉',
content: '确定取消投诉吗?', content: '确定取消投诉吗?',
@@ -112,18 +112,21 @@ export default {
clearComplain(id).then((res) => { clearComplain(id).then((res) => {
if (res.success) { if (res.success) {
Message.success('取消投诉成功'); Message.success('取消投诉成功');
this.getList(); getList();
} }
}); });
}, },
onCancel: () => { } onCancel: () => { }
}); });
}, }
goDetail (id) { // 跳转投诉详情
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">

View File

@@ -84,116 +84,124 @@
</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', defineOptions({ name: 'Favorites' });
props: {
const props = defineProps({
homePage: { homePage: {
type: Boolean, type: Boolean,
default: false default: false
} }
}, });
data() {
return { const route = useRoute();
favoriteWay: ['收藏商品', '收藏店铺'], // 收藏分类 const router = useRouter();
list: [], // 收藏列表
total: 0, // 收藏总数 const favoriteWay = ref(['收藏商品', '收藏店铺']); // 收藏分类
params: { // 请求参数 const list = ref([]); // 收藏列表
const total = ref(0); // 收藏总数
const params = reactive({ // 请求参数
pageNumber: 1, pageNumber: 1,
pageSize: 100, pageSize: 100,
type: this.$route.query.type || 'GOODS' type: route.query.type || 'GOODS'
}, });
spinShow: false // 加载状态 const spinShow = ref(false); // 加载状态
};
},
computed: {
activeTabIndex() {
return this.params.type === 'STORE' ? 1 : 0;
},
},
methods: { const activeTabIndex = computed(() => {
loadFavorites() { return params.type === 'STORE' ? 1 : 0;
if (this.params.type === 'STORE') { });
this.getStoreList();
function loadFavorites() {
if (params.type === 'STORE') {
getStoreList();
} else { } else {
this.getList(); getList();
} }
}, }
getList() { // 获取商品收藏列表
this.spinShow = true function getList() { // 获取商品收藏列表
collectList(this.params).then(res => { spinShow.value = true
collectList(params).then(res => {
if (res.success && res.result) { if (res.success && res.result) {
this.list = res.result.records || []; list.value = res.result.records || [];
} }
}).catch(() => { }).catch(() => {
this.list = []; list.value = [];
}).finally(() => { }).finally(() => {
this.spinShow = false spinShow.value = false
}) })
}, }
getStoreList() { // 获取店铺收藏列表
this.spinShow = true function getStoreList() { // 获取店铺收藏列表
storeCollectList(this.params).then(res => { spinShow.value = true
storeCollectList(params).then(res => {
if (res.success && res.result) { if (res.success && res.result) {
this.list = res.result.records || []; list.value = res.result.records || [];
} }
}).catch(() => { }).catch(() => {
this.list = []; list.value = [];
}).finally(() => { }).finally(() => {
this.spinShow = false spinShow.value = false
}) })
}, }
change(index) { // tab栏切换
if (index === 0) { this.params.type = 'GOODS', this.getList() } function change(index) { // tab栏切换
if (index === 1) { this.params.type = 'STORE', this.getStoreList() } if (index === 0) { params.type = 'GOODS', getList() }
}, if (index === 1) { params.type = 'STORE', getStoreList() }
cancel(id) { // 取消收藏 }
let typeName = this.params.type === 'GOODS' ? '商品' : '店铺'
function cancel(id) { // 取消收藏
let typeName = params.type === 'GOODS' ? '商品' : '店铺'
Modal.confirm({ Modal.confirm({
title: '取消收藏', title: '取消收藏',
content: `确定取消收藏该${typeName}吗?`, content: `确定取消收藏该${typeName}吗?`,
onOk: () => { onOk: () => {
cancelCollect(this.params.type, id).then(res => { cancelCollect(params.type, id).then(res => {
if (res.success) { if (res.success) {
this.getList(); getList();
} }
}) })
} }
}); });
}, }
cancelStore(id) { // 取消收藏
let typeName = this.params.type === 'GOODS' ? '商品' : '店铺' function cancelStore(id) { // 取消收藏
let typeName = params.type === 'GOODS' ? '商品' : '店铺'
Modal.confirm({ Modal.confirm({
title: '取消收藏', title: '取消收藏',
content: `确定取消收藏该${typeName}吗?`, content: `确定取消收藏该${typeName}吗?`,
onOk: () => { onOk: () => {
cancelStoreCollect(this.params.type, id).then(res => { cancelStoreCollect(params.type, id).then(res => {
if (res.success) { if (res.success) {
this.getStoreList(); getStoreList();
} }
}) })
} }
}); });
}, }
openItem (item) {
if (this.params.type === 'STORE') { function openItem (item) {
this.goShop(item.id) if (params.type === 'STORE') {
goShop(item.id)
return return
} }
this.buynow(item.skuId, item.goodsId) buynow(item.skuId, item.goodsId)
}, }
buynow(skuId, goodsId,storeId) { // 跳转详情
function buynow(skuId, goodsId, storeId) { // 跳转详情
let url let url
if (this.params.type === 'STORE') { if (params.type === 'STORE') {
url = this.$router.resolve({ url = router.resolve({
path: '/merchant', path: '/merchant',
query: { 'id': storeId } query: { 'id': storeId }
}) })
} else { } else {
url = this.$router.resolve({ url = router.resolve({
path: '/goodsDetail', path: '/goodsDetail',
query: { skuId, goodsId } query: { skuId, goodsId }
}) })
@@ -201,19 +209,19 @@ export default {
} }
window.open(url.href, '_blank') window.open(url.href, '_blank')
}, }
goShop(id) { // 跳转店铺页面
this.$router.push({ function goShop(id) { // 跳转店铺页面
router.push({
path: '/merchant', path: '/merchant',
query: { id } query: { id }
}) })
} }
},
mounted() { onMounted(() => {
if (this.homePage) this.params.pageSize = 5; if (props.homePage) params.pageSize = 5;
this.loadFavorites(); loadFavorites();
} });
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -74,43 +74,41 @@
</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,
params: {
pageNumber: 1, pageNumber: 1,
pageSize: 20 pageSize: 20
}, })
experienceLogs: {} const experienceLogs = ref({})
}
}, const rulesMap = computed(() => {
computed: {
rulesMap () {
const map = {} const map = {}
this.ruleList.forEach(item => { ruleList.value.forEach(item => {
map[item.ruleKey] = item.ruleName map[item.ruleKey] = item.ruleName
}) })
return map return map
}, })
sortedGradeList () {
const list = Array.isArray(this.gradeList) ? this.gradeList.slice() : [] const sortedGradeList = computed(() => {
const list = Array.isArray(gradeList.value) ? gradeList.value.slice() : []
list.sort((a, b) => { list.sort((a, b) => {
const sa = a && a.gradeSort != null ? Number(a.gradeSort) : 0 const sa = a && a.gradeSort != null ? Number(a.gradeSort) : 0
const sb = b && b.gradeSort != null ? Number(b.gradeSort) : 0 const sb = b && b.gradeSort != null ? Number(b.gradeSort) : 0
@@ -120,62 +118,68 @@ export default {
return ea - eb return ea - eb
}) })
return list return list
}, })
currentGradeIndex () {
if (!this.currentGradeId) return -1 const currentGradeIndex = computed(() => {
return this.sortedGradeList.findIndex(g => g && g.id === this.currentGradeId) if (!currentGradeId.value) return -1
}, return sortedGradeList.value.findIndex(g => g && g.id === currentGradeId.value)
nextGrade () { })
const idx = this.currentGradeIndex
const nextGrade = computed(() => {
const idx = currentGradeIndex.value
if (idx < 0) return null if (idx < 0) return null
return this.sortedGradeList[idx + 1] || null return sortedGradeList.value[idx + 1] || null
}, })
diffExperience () {
if (!this.nextGrade || this.nextGrade.requiredExperience == null) return 0 const diffExperience = computed(() => {
const diff = Number(this.nextGrade.requiredExperience) - Number(this.currentExperience || 0) if (!nextGrade.value || nextGrade.value.requiredExperience == null) return 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 []
const detail = gradeDetails.value.find(
(d) => d && d.grade && String(d.grade.id) === String(id) (d) => d && d.grade && String(d.grade.id) === String(id)
) )
const list = detail && Array.isArray(detail.benefits) ? detail.benefits : [] const list = detail && Array.isArray(detail.benefits) ? detail.benefits : []
return list.filter((b) => b && b.benefitState === 'OPEN') return list.filter((b) => b && b.benefitState === 'OPEN')
}, })
},
mounted () { onMounted(() => {
this.init() init()
}, })
methods: {
init () { function init () {
this.loadRules() loadRules()
this.loadGradeList() loadGradeList()
this.loadCurrent() loadCurrent()
}, }
loadRules () {
this.rulesLoading = true function loadRules () {
rulesLoading.value = true
memberGradeRules() memberGradeRules()
.then(res => { .then(res => {
if (res && res.success) { if (res && res.success) {
const items = res.result && Array.isArray(res.result.items) ? res.result.items : [] const items = res.result && Array.isArray(res.result.items) ? res.result.items : []
this.rulesDescription = (res.result && res.result.description) || '' rulesDescription.value = (res.result && res.result.description) || ''
this.ruleList = items ruleList.value = items
} }
}) })
.finally(() => { .finally(() => {
this.rulesLoading = false rulesLoading.value = false
}) })
}, }
loadGradeList () {
this.gradeLoading = true function loadGradeList () {
gradeLoading.value = true
memberGradeList() memberGradeList()
.then(res => { .then(res => {
if (res && res.success) { if (res && res.success) {
const raw = Array.isArray(res.result) ? res.result : [] const raw = Array.isArray(res.result) ? res.result : []
this.gradeDetails = raw gradeDetails.value = raw
.map((item) => { .map((item) => {
if (!item) return null if (!item) return null
if (item.grade != null) { if (item.grade != null) {
@@ -187,55 +191,61 @@ export default {
return { grade: item, benefits: [] } return { grade: item, benefits: [] }
}) })
.filter(Boolean) .filter(Boolean)
this.gradeList = this.gradeDetails.map((d) => d.grade).filter(Boolean) gradeList.value = gradeDetails.value.map((d) => d.grade).filter(Boolean)
} else { } else {
this.gradeDetails = [] gradeDetails.value = []
this.gradeList = [] gradeList.value = []
} }
}) })
.finally(() => { .finally(() => {
this.gradeLoading = false gradeLoading.value = false
}) })
}, }
loadCurrent () {
this.logLoading = true function loadCurrent () {
const params = { logLoading.value = true
pageNumber: this.params.pageNumber, const reqParams = {
pageSize: this.params.pageSize pageNumber: params.pageNumber,
pageSize: params.pageSize
} }
if (this.selectedRuleKey) params.ruleKey = this.selectedRuleKey if (selectedRuleKey.value) reqParams.ruleKey = selectedRuleKey.value
memberGradeCurrent(params) memberGradeCurrent(reqParams)
.then(res => { .then(res => {
if (res && res.success && res.result) { if (res && res.success && res.result) {
this.currentGrade = res.result.grade || {} currentGrade.value = res.result.grade || {}
this.currentExperience = res.result.experience || 0 currentExperience.value = res.result.experience || 0
this.currentGradeId = res.result.gradeId || (this.currentGrade && this.currentGrade.id) || '' currentGradeId.value = res.result.gradeId || (currentGrade.value && currentGrade.value.id) || ''
this.experienceLogs = res.result.experienceLogs || {} experienceLogs.value = res.result.experienceLogs || {}
} }
}) })
.finally(() => { .finally(() => {
this.logLoading = false logLoading.value = false
}) })
}, }
selectRule (ruleKey) {
this.selectedRuleKey = ruleKey || '' function selectRule (ruleKey) {
this.params.pageNumber = 1 selectedRuleKey.value = ruleKey || ''
this.loadCurrent() params.pageNumber = 1
}, loadCurrent()
changePage (val) { }
this.params.pageNumber = val
this.loadCurrent() function changePage (val) {
}, params.pageNumber = val
changePageSize (val) { loadCurrent()
this.params.pageSize = val }
this.params.pageNumber = 1
this.loadCurrent() function changePageSize (val) {
}, params.pageSize = val
getRuleName (ruleKey) { params.pageNumber = 1
loadCurrent()
}
function getRuleName (ruleKey) {
if (!ruleKey) return '-' if (!ruleKey) return '-'
return this.rulesMap[ruleKey] || ruleKey return rulesMap.value[ruleKey] || ruleKey
}, }
getRuleDesc (ruleKey) {
function getRuleDesc (ruleKey) {
const map = { const map = {
CONSUME: '客户消费1元可获取经验值,向下取整', CONSUME: '客户消费1元可获取经验值,向下取整',
REGISTER: '会员注册成功后可获取经验值', REGISTER: '会员注册成功后可获取经验值',
@@ -251,8 +261,9 @@ export default {
} }
if (!ruleKey) return '-' if (!ruleKey) return '-'
return map[ruleKey] || '-' return map[ruleKey] || '-'
}, }
getLogDeltaRaw (row) {
function getLogDeltaRaw (row) {
if (!row) return 0 if (!row) return 0
const candidate = row.variableExperience != null ? row.variableExperience : (row.changeExperience != null ? row.changeExperience : (row.experience != null ? row.experience : row.value)) const candidate = row.variableExperience != null ? row.variableExperience : (row.changeExperience != null ? row.changeExperience : (row.experience != null ? row.experience : row.value))
if (candidate == null) return 0 if (candidate == null) return 0
@@ -261,19 +272,20 @@ export default {
if (row.type === 'DECREASE') return -Math.abs(num) if (row.type === 'DECREASE') return -Math.abs(num)
if (row.type === 'INCREASE') return Math.abs(num) if (row.type === 'INCREASE') return Math.abs(num)
return num return num
}, }
getLogDeltaSign (row) {
const num = this.getLogDeltaRaw(row) function getLogDeltaSign (row) {
const num = getLogDeltaRaw(row)
return num >= 0 ? '+' : '-' return num >= 0 ? '+' : '-'
}, }
getLogDeltaAbsValue (row) {
const num = this.getLogDeltaRaw(row) function getLogDeltaAbsValue (row) {
const num = getLogDeltaRaw(row)
return Math.abs(num) return Math.abs(num)
}, }
getLogDeltaColor (row) {
return this.getLogDeltaRaw(row) >= 0 ? 'green' : 'red' function getLogDeltaColor (row) {
} return getLogDeltaRaw(row) >= 0 ? 'green' : 'red'
}
} }
</script> </script>

View File

@@ -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,39 +84,40 @@ 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位'}
@@ -123,8 +126,8 @@ export default {
{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'}
], ],
@@ -136,26 +139,23 @@ export default {
{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 () {
},
methods: {
loadMobile () {
const rawUserInfo = storage.getItem('userInfo'); const rawUserInfo = storage.getItem('userInfo');
if (rawUserInfo) { if (rawUserInfo) {
try { try {
const userInfo = JSON.parse(rawUserInfo); const userInfo = JSON.parse(rawUserInfo);
if (userInfo && userInfo.mobile) this.mobile = userInfo.mobile; if (userInfo && userInfo.mobile) mobile.value = userInfo.mobile;
} catch { } catch {
// 缓存的用户信息解析失败时忽略,改走接口获取 // 缓存的用户信息解析失败时忽略,改走接口获取
} }
@@ -164,24 +164,26 @@ export default {
getMemberMsg().then(res => { getMemberMsg().then(res => {
if (res.success && res.result) { if (res.success && res.result) {
storage.setItem('userInfo', res.result); storage.setItem('userInfo', res.result);
this.mobile = res.result.mobile || ''; mobile.value = res.result.mobile || '';
} }
}).catch((err) => { }).catch((err) => {
console.warn('获取会员信息失败', err); console.warn('获取会员信息失败', err);
}); });
}, }
normalizePwdError (message) {
function normalizePwdError (message) {
if (!message) return '修改失败,请稍后重试'; if (!message) return '修改失败,请稍后重试';
if (String(message).includes('旧密码') || String(message).includes('密码不正确')) { if (String(message).includes('旧密码') || String(message).includes('密码不正确')) {
return '旧密码不正确'; return '旧密码不正确';
} }
return message; return message;
}, }
// 修改
handleRegist () { // 修改
this.$refs['formRegister'].validate((valid) => { function handleRegist () {
formRegisterRef.value.validate((valid) => {
if (valid) { if (valid) {
const {newPassword, againPassword, password} = this.formRegister const {newPassword, againPassword, password} = formRegister
if (newPassword !== againPassword) { if (newPassword !== againPassword) {
Message.error({ Message.error({
content: '新密码和确认密码不一致' content: '新密码和确认密码不一致'
@@ -194,29 +196,31 @@ export default {
editPwd(params).then(res => { editPwd(params).then(res => {
if (res.success && res.result) { if (res.success && res.result) {
Message.success('修改密码成功'); Message.success('修改密码成功');
this.$router.push('/home') router.push('/home')
} else { } else {
Message.error(this.normalizePwdError(res.message)); Message.error(normalizePwdError(res.message));
} }
}).catch((err) => { }).catch((err) => {
Message.error(this.normalizePwdError(err?.message || err?.data?.message)); Message.error(normalizePwdError(err?.message || err?.data?.message));
}); });
} }
}) })
}, }
// 提交验证
submitRegister () { // 提交验证
this.$refs['formData'].validate((valid) => { function submitRegister () {
formDataRef.value.validate((valid) => {
if (valid) { if (valid) {
this.showPwd = true showPwd.value = true
} }
}) })
}, }
// 设置密码
setPassword () { // 设置密码
this.$refs['formDataUpdate'].validate((valid) => { function setPassword () {
formDataUpdateRef.value.validate((valid) => {
if (valid) { if (valid) {
const {newPassword, againPassword} = this.formDataUpdate const {newPassword, againPassword} = formDataUpdate
if (newPassword === '') { if (newPassword === '') {
Message.error({ Message.error({
content: '请输入密码' content: '请输入密码'
@@ -234,14 +238,17 @@ export default {
setPwd(params).then(res => { setPwd(params).then(res => {
if (res.message === 'success' && res.result) { if (res.message === 'success' && res.result) {
Message.success('支付密码设置成功'); Message.success('支付密码设置成功');
this.$router.push('/home') router.push('/home')
} }
}); });
} }
}) })
}
}
} }
onMounted(() => {
status.value = route.query.status
loadMobile()
});
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -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, // 请求接口加载框
params: { // 积分列表请求参数
pageNumber: 1, pageNumber: 1,
pageSize: 20 pageSize: 20
}, });
}
}, function getHistory() { // 获取积分历史
mounted () { memberPointHistory(params).then(res => {
this.getHistory() logData.value = res.result;
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 {

View File

@@ -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,66 +42,70 @@
</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: { // 验证规则
const formRef = ref(null);
const rules = { // 验证规则
nickName: [{required: true, message: '用户昵称不能为空'}, { max: 16, message: '用户昵称不能超过15个字符' }] nickName: [{required: true, message: '用户昵称不能为空'}, { max: 16, message: '用户昵称不能超过15个字符' }]
}, };
formItem: {}, // 表单数据 const formItem = ref({}); // 表单数据
action: buyerUrl + '/buyer/common/upload/file', // 上传接口 const action = buyerUrl + '/buyer/common/upload/file'; // 上传接口
accessToken: {} // 验证token const accessToken = reactive({}); // 验证token
}
}, onMounted(() => {
mounted () { accessToken.accessToken = storage.getItem('accessToken');
this.accessToken.accessToken = storage.getItem('accessToken'); loadUserInfo();
this.loadUserInfo(); });
},
methods: { function loadUserInfo () {
loadUserInfo () {
const rawUserInfo = storage.getItem('userInfo'); const rawUserInfo = storage.getItem('userInfo');
if (rawUserInfo) { if (rawUserInfo) {
this.applyUserInfo(JSON.parse(rawUserInfo)); applyUserInfo(JSON.parse(rawUserInfo));
return; return;
} }
if (!storage.getItem('accessToken')) { if (!storage.getItem('accessToken')) {
this.redirectToLogin(); redirectToLogin();
return; return;
} }
getMemberMsg().then(res => { getMemberMsg().then(res => {
if (res.success && res.result) { if (res.success && res.result) {
storage.setItem('userInfo', res.result); storage.setItem('userInfo', res.result);
this.applyUserInfo(res.result); applyUserInfo(res.result);
} else { } else {
this.redirectToLogin(); redirectToLogin();
} }
}).catch(() => { }).catch(() => {
this.redirectToLogin(); redirectToLogin();
}); });
}, }
applyUserInfo (userInfo) { function applyUserInfo (userInfo) {
this.formItem = { ...userInfo }; formItem.value = { ...userInfo };
this.formItem.birthday = this.normalizeBirthday(this.formItem.birthday); formItem.value.birthday = normalizeBirthday(formItem.value.birthday);
}, }
redirectToLogin () { function redirectToLogin () {
this.$router.push({ router.push({
path: '/login', path: '/login',
query: { query: {
rePath: this.$route.path, rePath: route.path,
query: JSON.stringify(this.$route.query || {}), query: JSON.stringify(route.query || {}),
}, },
}); });
}, }
normalizeBirthday (value) { function normalizeBirthday (value) {
if (!value) return '' if (!value) return ''
if (value instanceof Date) return value if (value instanceof Date) return value
if (typeof value === 'number') return new Date(value) if (typeof value === 'number') return new Date(value)
@@ -112,8 +116,8 @@ export default {
return isNaN(d.getTime()) ? '' : d return isNaN(d.getTime()) ? '' : d
} }
return '' return ''
}, }
formatBirthday (value) { function formatBirthday (value) {
if (!value) return null if (!value) return null
if (typeof value === 'string') { if (typeof value === 'string') {
const m = value.match(/^(\d{4})-(\d{2})-(\d{2})/) const m = value.match(/^(\d{4})-(\d{2})-(\d{2})/)
@@ -125,31 +129,28 @@ export default {
const mm = String(d.getMonth() + 1).padStart(2, '0') const mm = String(d.getMonth() + 1).padStart(2, '0')
const dd = String(d.getDate()).padStart(2, '0') const dd = String(d.getDate()).padStart(2, '0')
return `${yyyy}-${mm}-${dd}` return `${yyyy}-${mm}-${dd}`
}, }
save () { // 保存 function save () { // 保存
this.$refs.form.validate(valid => { formRef.value.validate(valid => {
if (valid) { if (valid) {
let params = { let params = {
birthday: this.formatBirthday(this.formItem.birthday), birthday: formatBirthday(formItem.value.birthday),
face: this.formItem.face, face: formItem.value.face,
nickName: this.formItem.nickName, nickName: formItem.value.nickName,
sex: this.formItem.sex sex: formItem.value.sex
} }
editMemberInfo(params).then(res => { editMemberInfo(params).then(res => {
if (res.success) { if (res.success) {
Message.success('修改个人资料成功') Message.success('修改个人资料成功')
storage.setItem('userInfo', res.result) storage.setItem('userInfo', res.result)
this.$router.go(0) router.go(0)
} }
}) })
} }
}) })
}, }
handleSuccess (res, file) { // 上传成功 function handleSuccess (res, file) { // 上传成功
this.formItem['face'] = res.result formItem.value['face'] = res.result
},
}
} }
</script> </script>

View File

@@ -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({}); // 订单商品
const form = reactive({ // 评分展示
deliveryScore: 5, deliveryScore: 5,
serviceScore: 5, serviceScore: 5,
descriptionScore: 5 descriptionScore: 5
}, // 表单 }); // 表单
visible: false, // 图片预览 const visible = ref(false); // 图片预览
action: buyerUrl + '/buyer/common/upload/file', // 上传地址 const action = buyerUrl + '/buyer/common/upload/file'; // 上传地址
accessToken: {}, // 验证token const accessToken = reactive({}); // 验证token
previewImage: '', // 预览图片地址 const previewImage = ref(''); // 预览图片地址
loading: false, // 提交加载状态 const loading = ref(false); // 提交加载状态
// el-rate SVG 通过 colors 写入 fill-color,跟随平台主题色 // el-rate SVG 通过 colors 写入 fill-color,跟随平台主题色
rateThemeColors: ['var(--theme-color)', 'var(--theme-color)', 'var(--theme-color)'] const rateThemeColors = ['var(--theme-color)', 'var(--theme-color)', 'var(--theme-color)'];
}
}, function getOrderDetail () { // 获取订单详情
methods: { orderDetail(route.query.sn).then(res => {
getOrderDetail () { // 获取订单详情 order.value = res.result
orderDetail(this.$route.query.sn).then(res => { orderGoods.value = res.result.orderItems[route.query.index]
this.order = res.result orderGoods.value['grade'] = 'GOOD'
this.orderGoods = res.result.orderItems[this.$route.query.index] orderGoods.value.uploadList = []
this.orderGoods['grade'] = 'GOOD'
this.orderGoods.uploadList = []
}) })
}, }
save () { // 保存评价
if (!this.form.serviceScore || !this.form.deliveryScore) { function save () { // 保存评价
if (!form.serviceScore || !form.deliveryScore) {
Message.warning('物流服务评价不能为空') Message.warning('物流服务评价不能为空')
return false; return false;
} }
if (!this.form.descriptionScore) { if (!form.descriptionScore) {
Message.warning('描述评价不能为空') Message.warning('描述评价不能为空')
return false; return false;
} }
if (!this.orderGoods.content || !String(this.orderGoods.content).trim()) { if (!orderGoods.value.content || !String(orderGoods.value.content).trim()) {
Message.warning('评论内容不能为空') Message.warning('评论内容不能为空')
return false; return false;
} }
this.loading = true; loading.value = true;
let goods = this.orderGoods let goods = orderGoods.value
let params = { let params = {
goodsId: goods.goodsId, goodsId: goods.goodsId,
orderItemSn: goods.sn, orderItemSn: goods.sn,
skuId: goods.skuId, skuId: goods.skuId,
descriptionScore: this.form.descriptionScore, descriptionScore: form.descriptionScore,
serviceScore: this.form.serviceScore, serviceScore: form.serviceScore,
deliveryScore: this.form.deliveryScore, deliveryScore: form.deliveryScore,
grade: goods.grade, grade: goods.grade,
content: goods.content.trim(), content: goods.content.trim(),
images: goods.uploadList.length ? goods.uploadList.join(',') : '' images: goods.uploadList.length ? goods.uploadList.join(',') : ''
} }
addEvaluation(params).then(res => { addEvaluation(params).then(res => {
this.loading = false loading.value = false
if (res.success) { if (res.success) {
Message.success('评价成功') Message.success('评价成功')
this.$router.push('/home/CommentList') router.push('/home/CommentList')
} else { } else {
Message.error(res.message || '评价失败,请稍后重试') Message.error(res.message || '评价失败,请稍后重试')
} }
}).catch((err) => { }).catch((err) => {
this.loading = false; loading.value = false;
Message.error(err?.message || '评价失败,请稍后重试') Message.error(err?.message || '评价失败,请稍后重试')
}) })
}, }
goGoodsDetail (skuId, goodsId) { // 跳转商品详情
let routerUrl = this.$router.resolve({ function goGoodsDetail (skuId, goodsId) { // 跳转商品详情
let routerUrl = router.resolve({
path: '/goodsDetail', path: '/goodsDetail',
query: {skuId, goodsId} query: {skuId, goodsId}
}) })
window.open(routerUrl.href, '_blank') window.open(routerUrl.href, '_blank')
}, }
handleView (name) { // 预览图片
this.previewImage = name; function handleView (name) { // 预览图片
this.visible = true; previewImage.value = name;
}, visible.value = true;
handleRemove (index) { // 移除图片 }
this.orderGoods.uploadList.splice(index, 1)
this.$forceUpdate() function handleRemove (index) { // 移除图片
}, orderGoods.value.uploadList.splice(index, 1)
handleSuccess (res, file) { // 上传成功回调 instance.proxy.$forceUpdate()
this.orderGoods.uploadList.push(res.result) }
this.$forceUpdate()
}, function handleSuccess (res, file) { // 上传成功回调
handleBeforeUpload () { // 上传之前钩子 orderGoods.value.uploadList.push(res.result)
const check = this.orderGoods.uploadList.length < 9; instance.proxy.$forceUpdate()
}
function handleBeforeUpload () { // 上传之前钩子
const check = orderGoods.value.uploadList.length < 9;
if (!check) { if (!check) {
Notice.warning({ Notice.warning({
title: '最多可以上传九张图片' title: '最多可以上传九张图片'
}); });
return check; 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 {

View File

@@ -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[this.orderGoods.grade] || '评价详情' return map[orderGoods.value.grade] || '评价详情'
} });
},
methods: { function getDetail() {
getDetail() {
// 获取评价详情 // 获取评价详情
evaluationDetail(this.$route.query.id).then((res) => { evaluationDetail(route.query.id).then((res) => {
if (res.success) this.orderGoods = res.result; if (res.success) orderGoods.value = res.result;
}); });
}, }
goGoodsDetail(skuId, goodsId) { function goGoodsDetail(skuId, goodsId) {
// 跳转商品详情 // 跳转商品详情
let routerUrl = this.$router.resolve({ let routerUrl = router.resolve({
path: "/goodsDetail", path: "/goodsDetail",
query: { skuId, goodsId }, query: { skuId, goodsId },
}); });
window.open(routerUrl.href, "_blank"); window.open(routerUrl.href, "_blank");
}, }
handleView(name) { function handleView(name) {
// 预览图片 // 预览图片
this.previewImage = name; previewImage.value = name;
this.visible = true; visible.value = true;
}, }
},
mounted() { onMounted(() => {
this.getDetail(); getDetail();
}, });
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.add-eval { .add-eval {

View File

@@ -22,46 +22,45 @@
</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
}
},
mounted () {
this.loadDetail()
},
methods: {
loadDetail () {
const id = this.$route.query.id
if (!id) { if (!id) {
this.loaded = true loaded.value = true
return return
} }
const cached = this.readCache(id) const cached = readCache(id)
if (cached) this.detail = cached if (cached) detail.value = cached
getMemberMessage(id).then(res => { getMemberMessage(id).then(res => {
if (res && res.success && res.result && res.result.id) { if (res && res.success && res.result && res.result.id) {
this.detail = res.result detail.value = res.result
sessionStorage.setItem(CACHE_KEY, JSON.stringify(res.result)) sessionStorage.setItem(CACHE_KEY, JSON.stringify(res.result))
} }
}).catch((err) => { }).catch((err) => {
console.warn('获取消息详情失败', err) console.warn('获取消息详情失败', err)
}).finally(() => { }).finally(() => {
this.loaded = true loaded.value = true
this.markRead() markRead()
}) })
}, }
readCache (id) {
function readCache (id) {
try { try {
const raw = sessionStorage.getItem(CACHE_KEY) const raw = sessionStorage.getItem(CACHE_KEY)
if (!raw) return null if (!raw) return null
@@ -70,38 +69,43 @@ export default {
} catch (e) { } catch (e) {
return null return null
} }
}, }
markRead () {
if (!this.detail.id || this.detail.status !== 'UN_READY') return function markRead () {
readMemberMsg(this.detail.id).then(res => { if (!detail.value.id || detail.value.status !== 'UN_READY') return
readMemberMsg(detail.value.id).then(res => {
if (res && res.success) { if (res && res.success) {
this.detail.status = 'ALREADY_READY' detail.value.status = 'ALREADY_READY'
refreshUnreadMessage() refreshUnreadMessage()
} }
}).catch(() => { }).catch(() => {
// 标记已读为尽力而为操作,失败可忽略 // 标记已读为尽力而为操作,失败可忽略
}) })
}, }
goBack () {
this.$router.push({ name: 'MsgList' }) function goBack () {
}, router.push({ name: 'MsgList' })
removeMessage () { }
function removeMessage () {
Modal.confirm({ Modal.confirm({
title: '确认删除', title: '确认删除',
content: '确认要删除此消息?', content: '确认要删除此消息?',
loading: true, loading: true,
onOk: () => onOk: () =>
delMemberMsg(this.detail.id).then((res) => { delMemberMsg(detail.value.id).then((res) => {
if (res.success) { if (res.success) {
Message.success('消息已成功放入回收站') Message.success('消息已成功放入回收站')
refreshUnreadMessage() refreshUnreadMessage()
this.goBack() goBack()
} }
}) })
}) })
}
}
} }
onMounted(() => {
loadDetail()
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@@ -47,67 +47,68 @@
</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({});
const status = ref(['未读', '已读', '回收站']);
const params = reactive({
pageNumber: 1, pageNumber: 1,
pageSize: 20, pageSize: 20,
status: 'UN_READY' status: 'UN_READY'
}, });
}
}, const activeStatusIndex = computed(() => {
computed: {
activeStatusIndex () {
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() }
}, function changePage (v) {
changePage (v) { params.pageNumber = v;
this.params.pageNumber = v; getList();
this.getList(); }
}, function changePageSize (v) {
changePageSize (v) { params.pageNumber = 1
this.params.pageNumber = 1 params.pageSize = v;
this.params.pageSize = v; getList();
this.getList(); }
}, function openDetail (row) {
openDetail (row) {
sessionStorage.setItem('buyer-member-message', JSON.stringify(row)) sessionStorage.setItem('buyer-member-message', JSON.stringify(row))
this.$router.push({ name: 'MsgDetail', query: { id: row.id } }) router.push({ name: 'MsgDetail', query: { id: row.id } })
}, }
getList () { function getList () {
memberMsgList(this.params).then(res => { memberMsgList(params).then(res => {
if (res.success) { if (res.success) {
this.messageData = res.result; messageData.value = res.result;
if (this.params.status === 'UN_READY') { if (params.status === 'UN_READY') {
this.$store.commit('SET_UNREAD_MSG', Number(res.result.total) || 0); appStore.setUnreadMsg(Number(res.result.total) || 0);
} }
} }
}) })
}, }
setRead (id) { function setRead (id) {
readMemberMsg(id).then(res => { readMemberMsg(id).then(res => {
if (res.success) { if (res.success) {
this.getList() getList()
} }
}) })
}, }
removeMessage (id) { function removeMessage (id) {
Modal.confirm({ Modal.confirm({
title: '确认删除', title: '确认删除',
content: '确认要删除此消息?', content: '确认要删除此消息?',
@@ -116,16 +117,15 @@ export default {
delMemberMsg(id).then((res) => { delMemberMsg(id).then((res) => {
if (res.success) { if (res.success) {
Message.success('消息已成功放入回收站'); Message.success('消息已成功放入回收站');
this.getList(); getList();
} }
}), }),
}); });
}
},
mounted () {
this.getList()
}
} }
onMounted(() => {
getList()
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.msg-table { .msg-table {

View File

@@ -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,15 +46,20 @@ import {
editMemberAddress, editMemberAddress,
getAddrDetail, getAddrDetail,
} from "@/api/address"; } from "@/api/address";
export default {
name: "AddAddress", defineOptions({ name: "AddAddress" });
data() {
return { const route = useRoute();
formData: { const router = useRouter();
const formRef = ref(null);
const mapRef = ref(null);
const formData = ref({
// 添加地址表单 // 添加地址表单
isDefault: false, isDefault: false,
}, });
ruleInline: { const ruleInline = {
// 验证规则 // 验证规则
name: [{ required: true, message: "请输入姓名", trigger: "blur" }], name: [{ required: true, message: "请输入姓名", trigger: "blur" }],
address: [{ required: true, message: "请输入地址", trigger: "change" }], address: [{ required: true, message: "请输入地址", trigger: "change" }],
@@ -67,85 +74,79 @@ export default {
message: "请输入正确的手机号", message: "请输入正确的手机号",
}, },
], ],
}, };
loading: false, // 加载状态 const loading = ref(false); // 加载状态
mapMsg: {}, // 地图信息 const mapMsg = ref({}); // 地图信息
};
}, // 保存地址
methods: { function save() {
save() { formRef.value.validate((valid) => {
// 保存地址
this.$refs.form.validate((valid) => {
if (valid) { if (valid) {
const params = JSON.parse(JSON.stringify(this.formData)); const params = JSON.parse(JSON.stringify(formData.value));
params.consigneeAddressPath = params.address.replace(/\s/g, ","); params.consigneeAddressPath = params.address.replace(/\s/g, ",");
delete params.address; delete params.address;
this.loading = true; loading.value = true;
if (this.$route.query.id) { if (route.query.id) {
editMemberAddress(params) editMemberAddress(params)
.then((res) => { .then((res) => {
this.loading = false; loading.value = false;
if (res.success) { if (res.success) {
this.$router.push("/home/MyAddress"); router.push("/home/MyAddress");
} }
}) })
.catch(() => { .catch(() => {
this.loading = false; loading.value = false;
}); });
} else { } else {
newMemberAddress(params) newMemberAddress(params)
.then((res) => { .then((res) => {
this.loading = false; loading.value = false;
if (res.success) { if (res.success) {
this.$router.push("/home/MyAddress"); router.push("/home/MyAddress");
} }
}) })
.catch(() => { .catch(() => {
this.loading = false; loading.value = false;
}); });
} }
} }
}); });
}, }
getAddrById(id) { function getAddrById(id) {
// 获取地址详情 // 获取地址详情
getAddrDetail(id).then((res) => { getAddrDetail(id).then((res) => {
if (res.success) { if (res.success) {
const data = res.result; const data = res.result;
data.address = res.result.consigneeAddressPath.replace(/,/g, " "); data.address = res.result.consigneeAddressPath.replace(/,/g, " ");
this.formData = data; formData.value = data;
} }
}); });
}, }
getAddress(val) { function getAddress(val) {
// 获取地图选择信息 // 获取地图选择信息
if(val.type === 'select'){ if(val.type === 'select'){
const paths = val.data.map(item => item.name).join(',') const paths = val.data.map(item => item.name).join(',')
const ids = val.data.map(item => item.id).join(',') const ids = val.data.map(item => item.id).join(',')
this.formData['address'] = paths formData.value['address'] = paths
this.formData['consigneeAddressIdPath'] = ids formData.value['consigneeAddressIdPath'] = ids
const coord = val.data[val.data.length - 1].center.split(',') const coord = val.data[val.data.length - 1].center.split(',')
this.formData.lat = coord[1] formData.value.lat = coord[1]
this.formData.lon = coord[0] formData.value.lon = coord[0]
}else{ }else{
this.formData["address"] = val.data.addr; formData.value["address"] = val.data.addr;
this.formData["consigneeAddressIdPath"] = val.data.addrId; formData.value["consigneeAddressIdPath"] = val.data.addrId;
this.formData["detail"] = val.data.address; formData.value["detail"] = val.data.address;
this.formData.lat = val.data.position.lat; formData.value.lat = val.data.position.lat;
this.formData.lon = val.data.position.lng; formData.value.lon = val.data.position.lng;
} }
}, }
},
mounted() { onMounted(() => {
const id = this.$route.query.id; const id = route.query.id;
if (id) this.getAddrById(id); if (id) getAddrById(id);
}, });
components: {
multipleMap
},
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -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,64 +131,61 @@
</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: { // 请求参数
const orderList = ref([]); // 订单列表
const params = reactive({ // 请求参数
pageNumber: 1, pageNumber: 1,
pageSize: 20, pageSize: 20,
keywords: '', keywords: '',
sn: '', sn: '',
sort: 'createTime', sort: 'createTime',
order: 'desc' order: 'desc'
}, });
// 状态数组 const total = ref(0); // 订单总数
afterSaleStatusList, const spinShow = ref(false); // 加载状态
total: 0, // 订单总数 const companyList = ref([]); // 物流公司列表
spinShow: false, // 加载状态 const logisticsShow = ref(false); // 物流信息modal
companyList: [], // 物流公司列表 const singleOrder = ref({}); // 单独的售后信息
logisticsShow: false, // 物流信息modal const form = reactive({ // 物流信息数据
singleOrder: {}, // 单独的售后信息
form: { // 物流信息数据
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, // 提交加载状态 const submitLoading = ref(false); // 提交加载状态
changeWay: [ const changeWay = [
{ name: '全部', status: '' }, { name: '全部', status: '' },
{ name: '申请中', status: 'APPLY' }, { name: '申请中', status: 'APPLY' },
{ name: '通过', status: 'PASS' }, { name: '通过', status: 'PASS' },
{ name: '完成售后', status: 'COMPLETE' }, { name: '完成售后', status: 'COMPLETE' },
], ];
activeTab: 0, const activeTab = ref(0);
};
}, function goDetail(sn) { // 跳转售后详情
mounted() { router.push({
this.getList();
},
methods: {
goDetail(sn) { // 跳转售后详情
this.$router.push({
name: 'AfterSaleDetail', name: 'AfterSaleDetail',
query: {sn} query: {sn}
}) })
}, }
cancel(sn) { // 取消售后申请 function cancel(sn) { // 取消售后申请
Modal.confirm({ Modal.confirm({
title: '取消', title: '取消',
content: '确定取消此次售后申请吗?', content: '确定取消此次售后申请吗?',
@@ -196,105 +193,107 @@ export default {
cancelAfterSale(sn).then(res => { cancelAfterSale(sn).then(res => {
if (res.success) { if (res.success) {
Message.success('取消售后申请成功') Message.success('取消售后申请成功')
this.getList() getList()
} }
}) })
}, },
onCancel: () => { onCancel: () => {
} }
}); });
}, }
goodsDetail(skuId, goodsId) { function goodsDetail(skuId, goodsId) {
// 跳转商品详情 // 跳转商品详情
let routeUrl = this.$router.resolve({ let routeUrl = router.resolve({
path: '/goodsDetail', path: '/goodsDetail',
query: {skuId, goodsId} query: {skuId, goodsId}
}); });
window.open(routeUrl.href, '_blank'); window.open(routeUrl.href, '_blank');
}, }
// 跳转店铺首页 // 跳转店铺首页
shopPage(id) { function shopPage(id) {
this.$router.push({ router.push({
path: '/merchant', path: '/merchant',
query: {id: id} query: {id: id}
}); });
}, }
change(index) { function change(index) {
this.activeTab = index activeTab.value = index
this.params.pageNumber = 1 params.pageNumber = 1
const current = this.changeWay[index] const current = changeWay[index]
if (!current || !current.status) { if (!current || !current.status) {
delete this.params.serviceStatus delete params.serviceStatus
} else { } else {
this.params.serviceStatus = current.status params.serviceStatus = current.status
} }
this.getList() getList()
}, }
getList() { // 获取售后列表 function getList() { // 获取售后列表
this.spinShow = true; spinShow.value = true;
let params = JSON.parse(JSON.stringify(this.params)) let paramsData = JSON.parse(JSON.stringify(params))
afterSaleList(params).then(res => { afterSaleList(paramsData).then(res => {
this.spinShow = false spinShow.value = false
if (res.success) { if (res.success) {
this.orderList = res.result.records; orderList.value = res.result.records;
this.total = res.result.total; total.value = res.result.total;
} }
}); });
}, }
changePageNum(val) { // 修改页码 function changePageNum(val) { // 修改页码
this.params.pageNumber = val; params.pageNumber = val;
this.getList() getList()
}, }
changePageSize(val) { // 修改页数 function changePageSize(val) { // 修改页数
this.params.pageNumber = 1; params.pageNumber = 1;
this.params.pageSize = val; params.pageSize = val;
this.getList() getList()
}, }
filterOrderStatus(status) { // 获取订单状态中文 function filterOrderStatus(status) { // 获取订单状态中文
const ob = this.afterSaleStatusList.filter(e => { const ob = afterSaleStatusList.filter(e => {
return e.status === status return e.status === status
}); });
return ob && ob[0] ? ob[0].name : status return ob && ob[0] ? ob[0].name : status
}, }
// 获取物流公司列表 // 获取物流公司列表
getCompany() { function getCompany() {
getLogisticsCompany().then(res => { getLogisticsCompany().then(res => {
if (res.success) { if (res.success) {
this.companyList = res.result companyList.value = res.result
} }
}) })
}, }
// 提交物流信息 // 提交物流信息
submitDelivery() { function submitDelivery() {
this.$refs.form?.validate((valid) => { formRef.value?.validate((valid) => {
if (!valid) return; if (!valid) return;
this.submitLoading = true; submitLoading.value = true;
afterSaleDelivery(this.form).then(res => { afterSaleDelivery(form).then(res => {
if (res.success) { if (res.success) {
this.logisticsShow = false; logisticsShow.value = false;
Message.success('提交成功'); Message.success('提交成功');
this.getList(); getList();
} }
this.submitLoading = false; submitLoading.value = false;
}).catch(() => { }).catch(() => {
this.submitLoading = false; submitLoading.value = false;
}); });
}); });
}, }
// 提交物流modal // 提交物流modal
openModal(row) { function openModal(row) {
this.singleOrder = row; singleOrder.value = row;
this.logisticsShow = true; logisticsShow.value = true;
if (!this.companyList.length) { if (!companyList.value.length) {
this.getCompany(); getCompany();
} }
this.$nextTick(() => { nextTick(() => {
this.$refs.form?.resetFields(); formRef.value?.resetFields();
this.form.afterSaleSn = row.sn; form.afterSaleSn = row.sn;
}); });
} }
}
}; onMounted(() => {
getList();
});
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -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,97 +135,99 @@
</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
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 // 提交加载状态 const submitLoading = ref(false); // 提交加载状态
};
}, const formRef = ref(null);
computed: {
canSubmitLogistics () { const canSubmitLogistics = computed(() => {
return this.afterSale.serviceStatus == 'PASS' && this.afterSale.serviceType != 'RETURN_MONEY' return afterSale.value.serviceStatus == 'PASS' && afterSale.value.serviceType != 'RETURN_MONEY'
}, });
canCancel () { const canCancel = computed(() => {
return this.afterSale.afterSaleAllowOperationVO && this.afterSale.afterSaleAllowOperationVO.cancel return afterSale.value.afterSaleAllowOperationVO && afterSale.value.afterSaleAllowOperationVO.cancel
} });
},
methods: { function getDetail () { // 售后详情
getDetail () { // 售后详情 afterSaleDetail(route.query.sn).then(res => {
afterSaleDetail(this.$route.query.sn).then(res => {
if (res.success) { if (res.success) {
this.afterSale = res.result; afterSale.value = res.result;
this.afterSale.serviceName = this.filterOrderStatus(this.afterSale.serviceStatus) afterSale.value.serviceName = filterOrderStatus(afterSale.value.serviceStatus)
// 如果是原因id,则调接口查询原因中文释义 // 如果是原因id,则调接口查询原因中文释义
const pattern3 = new RegExp('[0-9]+'); const pattern3 = new RegExp('[0-9]+');
if (pattern3.test(this.afterSale.reason)) { if (pattern3.test(afterSale.value.reason)) {
this.getReason(this.afterSale.serviceType) getReason(afterSale.value.serviceType)
} else { } else {
this.afterSale['reasonName'] = this.afterSale.reason afterSale.value['reasonName'] = afterSale.value.reason
} }
} }
}) })
}, }
getReason (type) { // 获取售后原因 function getReason (type) { // 获取售后原因
afterSaleReason(type).then(res => { afterSaleReason(type).then(res => {
if (res.success) { if (res.success) {
this.reasonList = res.result reasonList.value = res.result
this.reasonList.forEach(e => { reasonList.value.forEach(e => {
if (e.id === this.afterSale.reason) { if (e.id === afterSale.value.reason) {
this.afterSale['reasonName'] = e.reason afterSale.value['reasonName'] = e.reason
} }
}) })
} }
}) })
}, }
getLog () { // 获取售后日志 function getLog () { // 获取售后日志
afterSaleLog(this.$route.query.sn).then(res => { afterSaleLog(route.query.sn).then(res => {
this.logList = (res.result || []).slice().sort((a, b) => { logList.value = (res.result || []).slice().sort((a, b) => {
return new Date(a.createTime).getTime() - new Date(b.createTime).getTime() return new Date(a.createTime).getTime() - new Date(b.createTime).getTime()
}) })
}) })
}, }
logDesc (log) { function logDesc (log) {
const operator = log.operatorName ? `操作人:${log.operatorName}` : '' const operator = log.operatorName ? `操作人:${log.operatorName}` : ''
return [operator, log.createTime].filter(Boolean).join(' ') return [operator, log.createTime].filter(Boolean).join(' ')
}, }
shopPage (id) { function shopPage (id) {
if (!id) return if (!id) return
this.$router.push({ router.push({
path: '/merchant', path: '/merchant',
query: { id } query: { id }
}) })
}, }
filterOrderStatus (status) { // 获取订单状态中文 function filterOrderStatus (status) { // 获取订单状态中文
const ob = this.afterSaleStatusList.filter(e => { return e.status === status }); const ob = afterSaleStatusList.filter(e => { return e.status === status });
if (ob.length) return ob[0].name if (ob.length) return ob[0].name
}, }
perviewImg (img) { // 查看图片 function perviewImg (img) { // 查看图片
window.open(img, '_blank') window.open(img, '_blank')
}, }
cancel (sn) { // 取消售后申请 function cancel (sn) { // 取消售后申请
Modal.confirm({ Modal.confirm({
title: '取消', title: '取消',
content: '<p>确定取消此次售后申请吗?</p>', content: '<p>确定取消此次售后申请吗?</p>',
@@ -233,52 +235,51 @@ export default {
cancelAfterSale(sn).then(res => { cancelAfterSale(sn).then(res => {
if (res.success) { if (res.success) {
Message.success('取消售后申请成功') Message.success('取消售后申请成功')
this.getDetail() getDetail()
} }
}) })
}, },
onCancel: () => {} onCancel: () => {}
}); });
}, }
// 获取物流公司列表 // 获取物流公司列表
getCompany () { function getCompany () {
getLogisticsCompany().then(res => { getLogisticsCompany().then(res => {
if (res.success) { if (res.success) {
this.companyList = res.result companyList.value = res.result
} }
}) })
}, }
// 提交物流信息 // 提交物流信息
submitDelivery () { function submitDelivery () {
this.submitLoading = true submitLoading.value = true
afterSaleDelivery(this.form).then(res => { afterSaleDelivery(form).then(res => {
if (res.success) { if (res.success) {
this.logisticsShow = false; logisticsShow.value = false;
Message.success('提交成功') Message.success('提交成功')
this.getDetail() getDetail()
} }
this.submitLoading = false submitLoading.value = false
}).catch(() => { }).catch(() => {
this.submitLoading = false submitLoading.value = false
}) })
}, }
// 提交物流modal // 提交物流modal
openModal () { function openModal () {
this.logisticsShow = true; logisticsShow.value = true;
if (!this.companyList.length) { if (!companyList.value.length) {
this.getCompany(); getCompany();
} }
this.$nextTick(() => { nextTick(() => {
this.$refs.form?.resetFields(); formRef.value?.resetFields();
this.form.afterSaleSn = this.afterSale.sn; form.afterSaleSn = afterSale.value.sn;
}); });
} }
},
mounted () { onMounted(() => {
this.getDetail(); getDetail();
this.getLog() getLog()
} });
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.order-detail { .order-detail {

View File

@@ -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,41 +101,46 @@
</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) => {
const formRef = ref(null);
const goodsData = ref([]); // 商品数据
const reasonList = ref([]); // 售后原因列表
const info = ref({}); // 售后信息
const form = reactive({ // 售后数据
serviceType: 'RETURN_GOODS',
num: 1
});
const uploadList = ref([]); // 上传列表
const action = buyerUrl + '/buyer/common/upload/file'; // 上传地址
const accessToken = reactive({}); // 验证token
const visible = ref(false); // 图片预览
const previewImage = ref(''); // 预览图片url
const checkNum = (rule, value, callback) => {
if (value === '') { if (value === '') {
callback(new Error('请填写提交数量')); callback(new Error('请填写提交数量'));
} else if (value > this.info.num) { } else if (value > info.value.num) {
callback(new Error('提交数量不能大于购买数量')); callback(new Error('提交数量不能大于购买数量'));
} else if (!RegExp.integer.test(value)) { } else if (!RegExp.integer.test(value)) {
callback(new Error('提交数量必须为正整数')); callback(new Error('提交数量必须为正整数'));
} else { } else {
callback(); callback();
} }
}; };
return { const rules = { // 验证规则
goodsData: [], // 商品数据
reasonList: [], // 售后原因列表
info: {}, // 售后信息
form: { // 售后数据
serviceType: 'RETURN_GOODS',
num: 1
},
uploadList: [], // 上传列表
action: buyerUrl + '/buyer/common/upload/file', // 上传地址
accessToken: {}, // 验证token
visible: false, // 图片预览
previewImage: '', // 预览图片url
rules: { // 验证规则
num: [{ validator: checkNum }], num: [{ validator: checkNum }],
reason: [{ required: true, message: '请选择提交原因' }], reason: [{ required: true, message: '请选择提交原因' }],
problemDesc: [{ required: true, message: '请输入问题描述' }], problemDesc: [{ required: true, message: '请输入问题描述' }],
@@ -149,77 +154,72 @@ export default {
], ],
bankAccountName: [{ required: true, message: '请输入银行开户名' }], bankAccountName: [{ required: true, message: '请输入银行开户名' }],
bankDepositName: [{ required: true, message: '请输入银行开户行' }] bankDepositName: [{ required: true, message: '请输入银行开户行' }]
} };
}
}, function getInfo () { // 获取售后信息
methods: { afterSaleInfo(route.query.sn).then(res => {
getInfo () { // 获取售后信息
afterSaleInfo(this.$route.query.sn).then(res => {
if (res.success) { if (res.success) {
this.info = res.result info.value = res.result
this.goodsData.push(res.result) goodsData.value.push(res.result)
if (!this.info.returnGoods && this.info.returnMoney) { if (!info.value.returnGoods && info.value.returnMoney) {
this.form.serviceType = 'RETURN_MONEY' form.serviceType = 'RETURN_MONEY'
} }
this.getReason(this.form.serviceType) getReason(form.serviceType)
} }
}) })
}, }
getReason (type) { // 获取售后原因 function getReason (type) { // 获取售后原因
afterSaleReason(type).then(res => { afterSaleReason(type).then(res => {
if (res.success) this.reasonList = res.result if (res.success) reasonList.value = res.result
}) })
}, }
changeReason (type) { // 改变售后原因列表 function changeReason (type) { // 改变售后原因列表
this.form.reason = undefined form.reason = undefined
this.getReason(type) getReason(type)
this.$nextTick(() => { nextTick(() => {
this.$refs.form?.clearValidate('reason') formRef.value?.clearValidate('reason')
}) })
}, }
apply () { // 售后申请提交 function apply () { // 售后申请提交
this.$refs.form.validate(valid => { formRef.value.validate(valid => {
if (valid) { if (valid) {
let params = Object.assign(this.info, this.form) let params = Object.assign(info.value, form)
params.images = this.uploadList.toString() params.images = uploadList.value.toString()
params.orderItemSn = this.$route.query.sn params.orderItemSn = route.query.sn
params.reason = this.reasonList.find(item => item.id == params.reason).reason params.reason = reasonList.value.find(item => item.id == params.reason).reason
applyAfterSale(params).then(res => { applyAfterSale(params).then(res => {
if (res.success) { if (res.success) {
Message.success('售后申请提交成功,请到售后订单查看!') Message.success('售后申请提交成功,请到售后订单查看!')
this.$router.push({name: 'AfterSale'}) router.push({name: 'AfterSale'})
} }
}) })
} }
}) })
}, }
handleView (name) { // 预览图片 function handleView (name) { // 预览图片
this.previewImage = name; previewImage.value = name;
this.visible = true; visible.value = true;
}, }
handleRemove (index) { // 移除图片 function handleRemove (index) { // 移除图片
this.uploadList.splice(index, 1) uploadList.value.splice(index, 1)
this.$forceUpdate() }
}, function handleSuccess (res, file) { // 上传成功回调
handleSuccess (res, file) { // 上传成功回调 uploadList.value.push(res.result)
this.uploadList.push(res.result) }
this.$forceUpdate() function handleBeforeUpload () { // 上传之前钩子函数
}, const check = uploadList.value.length < 6;
handleBeforeUpload () { // 上传之前钩子函数
const check = this.uploadList.length < 6;
if (!check) { if (!check) {
Notice.warning({ Notice.warning({
title: '最多可以上传5张图片' title: '最多可以上传5张图片'
}); });
return check; return check;
} }
}
},
mounted () {
this.accessToken.accessToken = storage.getItem('accessToken');
this.getInfo()
}
} }
onMounted(() => {
accessToken.accessToken = storage.getItem('accessToken');
getInfo()
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@@ -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({}); // 订单商品
const form = reactive({ // 投诉表单
complainTopic: '', complainTopic: '',
content: '' content: ''
}, // 表单 }); // 表单
visible: false, // 图片预览 const visible = ref(false); // 图片预览
action: buyerUrl + '/buyer/common/upload/file', // 上传地址 const action = ref(buyerUrl + '/buyer/common/upload/file'); // 上传地址
accessToken: {}, // 验证token const accessToken = reactive({}); // 验证token
previewImage: '', // 图片地址 const previewImage = ref(''); // 图片地址
loading: false, // 加载状态 const loading = ref(false); // 加载状态
reasonList: [] // 投诉原因 const reasonList = ref([]); // 投诉原因
}
}, function getOrderDetail() { // 获取订单详情
methods: { orderDetail(route.query.sn).then(res => {
getOrderDetail () { // 获取订单详情 order.value = res.result
orderDetail(this.$route.query.sn).then(res => { orderGoods.value = res.result.orderItems[route.query.index]
this.order = res.result orderGoods.value.uploadList = []
this.orderGoods = res.result.orderItems[this.$route.query.index]
this.orderGoods.uploadList = []
}) })
afterSaleReason('COMPLAIN').then(res => { afterSaleReason('COMPLAIN').then(res => {
if (res.success) { if (res.success) {
this.reasonList = res.result reasonList.value = res.result
this.form['complainTopic'] = res.result[0].reason form['complainTopic'] = res.result[0].reason
this.$forceUpdate() instance.proxy.$forceUpdate()
} }
}) })
}, }
save () { // 提交投诉信息
if (!this.form.content || !String(this.form.content).trim()) { function save() { // 提交投诉信息
if (!form.content || !String(form.content).trim()) {
Message.warning('投诉内容不能为空') Message.warning('投诉内容不能为空')
return return
} }
let params = { let params = {
goodsId: this.orderGoods.goodsId, goodsId: orderGoods.value.goodsId,
complainTopic: this.form.complainTopic, complainTopic: form.complainTopic,
content: this.form.content, content: form.content,
images: this.orderGoods.uploadList.toString(), images: orderGoods.value.uploadList.toString(),
orderSn: this.$route.query.sn, orderSn: route.query.sn,
skuId: this.orderGoods.skuId skuId: orderGoods.value.skuId
} }
handleComplain(params).then(res => { handleComplain(params).then(res => {
if (res.success) { if (res.success) {
Message.success('投诉申请已提交,感谢您的反馈') Message.success('投诉申请已提交,感谢您的反馈')
this.$router.push({name: 'ComplainList'}) router.push({name: 'ComplainList'})
} }
}) })
}, }
goGoodsDetail (skuId, goodsId) { // 跳转商品详情
let routerUrl = this.$router.resolve({ function goGoodsDetail(skuId, goodsId) { // 跳转商品详情
let routerUrl = router.resolve({
path: '/goodsDetail', path: '/goodsDetail',
query: {skuId, goodsId} query: {skuId, goodsId}
}) })
window.open(routerUrl.href, '_blank') window.open(routerUrl.href, '_blank')
}, }
handleView (name) { // 预览图片
this.previewImage = name; function handleView(name) { // 预览图片
this.visible = true; previewImage.value = name;
}, visible.value = true;
handleRemove (index) { // 移除图片 }
this.orderGoods.uploadList.splice(index, 1)
this.$forceUpdate() function handleRemove(index) { // 移除图片
}, orderGoods.value.uploadList.splice(index, 1)
handleSuccess (res, file) { // 上传成功回调 instance.proxy.$forceUpdate()
this.orderGoods.uploadList.push(res.result) }
this.$forceUpdate()
}, function handleSuccess(res, file) { // 上传成功回调
handleBeforeUpload () { // 上传之前钩子函数 orderGoods.value.uploadList.push(res.result)
const check = this.orderGoods.uploadList.length < 6; instance.proxy.$forceUpdate()
}
function handleBeforeUpload() { // 上传之前钩子函数
const check = orderGoods.value.uploadList.length < 6;
if (!check) { if (!check) {
Notice.warning({ Notice.warning({
title: '最多可以上传五张图片' title: '最多可以上传五张图片'
}); });
return check; return check;
} }
}
},
mounted () {
this.accessToken.accessToken = storage.getItem('accessToken');
this.getOrderDetail()
window.scrollTo(0, 0)
}
} }
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 {

View File

@@ -35,30 +35,30 @@
</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: '你确定删除这个收货地址',
@@ -66,7 +66,7 @@ export default {
delMemberAddress(id).then((res) => { delMemberAddress(id).then((res) => {
if (res.success) { if (res.success) {
Message.success('删除成功'); Message.success('删除成功');
this.getAddrList(); getAddrList();
} }
}); });
}, },
@@ -74,20 +74,20 @@ export default {
Message.info('取消删除'); Message.info('取消删除');
} }
}); });
}, }
getAddrList () {
// 获取地址列表 // 获取地址列表
function getAddrList () {
memberAddress().then((res) => { memberAddress().then((res) => {
if (res.success) { if (res.success) {
this.list = res.result.records; list.value = res.result.records;
} }
}); });
} }
},
mounted () { onMounted(() => {
this.getAddrList(); getAddrList();
} });
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -197,63 +197,49 @@
</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: {
const props = defineProps({
homePage: { // 判断是否个人中心首页展示内容 homePage: { // 判断是否个人中心首页展示内容
type: Boolean, type: Boolean,
default: false default: false
} }
}, });
data () {
return { const route = useRoute();
orderList: [], // 订单列表 const router = useRouter();
params: { // 请求参数
const orderList = ref([]); // 订单列表
const params = reactive({ // 请求参数
pageNumber: 1, pageNumber: 1,
pageSize: 20, pageSize: 20,
// orderStatus: 'ALL', // orderStatus: 'ALL',
keywords: '', keywords: '',
tag: 'ALL' tag: 'ALL'
}, });
cancelParams: { // 取消售后参数 const cancelParams = reactive({ // 取消售后参数
orderSn: '', orderSn: '',
reason: '' reason: ''
}, });
// 状态数组 const changeWay = ref(['全部', '待付款', '待发货', '待收货/使用']);
orderStatusList, const activeOrderTab = ref(0);
afterSaleStatusList, const total = ref(0); // 数据总数
changeWay: ['全部', '待付款', '待发货', '待收货/使用'], const spinShow = ref(false); // 加载状态
activeOrderTab: 0, const afterSaleModal = ref(false); // 选择售后商品模态框
total: 0, // 数据总数 const afterSaleArr = ref([]); // 售后商品列表
spinShow: false, // 加载状态 const cancelAvail = ref(false); // 取消订单modal控制
afterSaleModal: false, // 选择售后商品模态框 const cancelReason = ref([]); // 取消订单原因
afterSaleArr: [], // 售后商品列表
cancelAvail: false, // 取消订单modal控制 // 退款状态枚举
cancelReason: [] // 取消订单原因 function refundPriceList(status) {
};
},
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) { switch (status) {
case 'ALL_REFUND': case 'ALL_REFUND':
return "全部退款"; return "全部退款";
@@ -266,67 +252,67 @@ export default {
default: default:
return ""; return "";
} }
}, }
goodsDetail (skuId, goodsId) { function goodsDetail (skuId, goodsId) {
// 跳转商品详情 // 跳转商品详情
let routeUrl = this.$router.resolve({ let routeUrl = router.resolve({
path: '/goodsDetail', path: '/goodsDetail',
query: { skuId, goodsId } query: { skuId, goodsId }
}); });
window.open(routeUrl.href, '_blank'); window.open(routeUrl.href, '_blank');
}, }
orderTags () { function orderTags () {
return ['ALL', 'WAIT_PAY', 'WAIT_SHIP', 'WAIT_USE']; return ['ALL', 'WAIT_PAY', 'WAIT_SHIP', 'WAIT_USE'];
}, }
applyRouteTag () { function applyRouteTag () {
const tags = this.orderTags(); const tags = orderTags();
const tag = this.$route.query.tag; const tag = route.query.tag;
const index = tags.indexOf(tag); const index = tags.indexOf(tag);
this.activeOrderTab = index > -1 ? index : 0; activeOrderTab.value = index > -1 ? index : 0;
this.params.tag = tags[this.activeOrderTab]; params.tag = tags[activeOrderTab.value];
this.params.pageNumber = 1; params.pageNumber = 1;
}, }
// 切换订单状态 // 切换订单状态
change (index) { function change (index) {
this.activeOrderTab = index; activeOrderTab.value = index;
this.params.pageNumber = 1; params.pageNumber = 1;
delete this.params.commentStatus; delete params.commentStatus;
const tags = this.orderTags(); const tags = orderTags();
this.params.tag = tags[index] || 'ALL'; params.tag = tags[index] || 'ALL';
const query = { ...this.$route.query }; const query = { ...route.query };
if (index === 0) delete query.tag; if (index === 0) delete query.tag;
else query.tag = this.params.tag; else query.tag = params.tag;
const current = this.$route.query.tag || 'ALL'; const current = route.query.tag || 'ALL';
if (current !== this.params.tag) { if (current !== params.tag) {
this.$router.replace({ name: 'MyOrder', query }); router.replace({ name: 'MyOrder', query });
} }
this.getList() getList()
}, }
// 跳转店铺首页 // 跳转店铺首页
shopPage (id) { function shopPage (id) {
this.$router.push({ router.push({
path: '/merchant', path: '/merchant',
query: { id: id } query: { id: id }
}); });
}, }
orderDetail (sn) { function orderDetail (sn) {
this.$router.push({ name: 'OrderDetail', query: {sn} }); router.push({ name: 'OrderDetail', query: {sn} });
}, }
goAfterSaleDetail (sn) { function goAfterSaleDetail (sn) {
this.$router.push({ name: 'AfterSaleDetail', query: { sn } }); router.push({ name: 'AfterSaleDetail', query: { sn } });
}, }
received (sn) { // 确认收货 function received (sn) { // 确认收货
sureReceived(sn).then(res => { sureReceived(sn).then(res => {
if (res.success) { if (res.success) {
Message.success('确认收货成功') Message.success('确认收货成功')
this.getList() getList()
} }
}) })
}, }
goPay (sn) { // 去支付 function goPay (sn) { // 去支付
this.$router.push({path: '/payment', query: {orderType: 'ORDER', sn}}); router.push({path: '/payment', query: {orderType: 'ORDER', sn}});
}, }
applyAfterSale (goodsItem) { // 申请售后 function applyAfterSale (goodsItem) { // 申请售后
let arr = [] let arr = []
goodsItem.forEach(e => { goodsItem.forEach(e => {
if (e.afterSaleStatus === 'NOT_APPLIED'|| e.afterSaleStatus === 'PART_AFTER_SALE') { if (e.afterSaleStatus === 'NOT_APPLIED'|| e.afterSaleStatus === 'PART_AFTER_SALE') {
@@ -334,49 +320,49 @@ export default {
} }
}); });
if (arr.length === 1) { if (arr.length === 1) {
this.$router.push({name: 'ApplyAfterSale', query: {sn: arr[0].sn}}) router.push({name: 'ApplyAfterSale', query: {sn: arr[0].sn}})
} else { } else {
this.afterSaleArr = arr; afterSaleArr.value = arr;
this.afterSaleModal = true afterSaleModal.value = true
} }
}, }
// 申请售后 // 申请售后
afterSaleSelect (item) { function afterSaleSelect (item) {
this.$router.push({name: 'ApplyAfterSale', query: {sn: item.sn}}) router.push({name: 'ApplyAfterSale', query: {sn: item.sn}})
}, }
complain (sn, goodsIndex) { // 投诉 function complain (sn, goodsIndex) { // 投诉
this.$router.push({name: 'Complain', query: {sn, index: goodsIndex}}) router.push({name: 'Complain', query: {sn, index: goodsIndex}})
}, }
delOrder (sn) { // 删除订单 function delOrder (sn) { // 删除订单
Modal.confirm({ Modal.confirm({
title: '删除订单', title: '删除订单',
content: '<p>确认删除当前订单吗?</p>', content: '<p>确认删除当前订单吗?</p>',
onOk: () => { onOk: () => {
delOrder(sn).then(res => { deleteOrder(sn).then(res => {
if (res.success) { if (res.success) {
Message.success('删除成功'); Message.success('删除成功');
this.getList() getList()
} }
}) })
}, },
onCancel: () => {} onCancel: () => {}
}); });
}, }
getList () { function getList () {
this.spinShow = true; spinShow.value = true;
let params = JSON.parse(JSON.stringify(this.params)) let _params = JSON.parse(JSON.stringify(params))
if (params.orderStatus === 'ALL') { if (_params.orderStatus === 'ALL') {
delete params.orderStatus delete _params.orderStatus
} }
getOrderList(params).then(res => { getOrderList(_params).then(res => {
this.spinShow = false spinShow.value = false
if (res.success) { if (res.success) {
this.orderList = res.result.records; orderList.value = res.result.records;
this.total = res.result.total; total.value = res.result.total;
} }
}); });
}, }
mapAfterSaleOrder (item) { function mapAfterSaleOrder (item) {
return { return {
...item, ...item,
isAfterSale: true, isAfterSale: true,
@@ -396,51 +382,51 @@ export default {
], ],
allowOperationVO: {}, allowOperationVO: {},
} }
}, }
changePageNum (val) { // 修改页码 function changePageNum (val) { // 修改页码
this.params.pageNumber = val; params.pageNumber = val;
this.getList() getList()
}, }
changePageSize (val) { // 修改页数 function changePageSize (val) { // 修改页数
this.params.pageNumber = 1; params.pageNumber = 1;
this.params.pageSize = val; params.pageSize = val;
this.getList() getList()
}, }
handleCancelOrder (sn) { function handleCancelOrder (sn) {
// 取消订单 // 取消订单
this.cancelParams.orderSn = sn; cancelParams.orderSn = sn;
afterSaleReason('CANCEL').then(res => { afterSaleReason('CANCEL').then(res => {
if (res.success) { if (res.success) {
this.cancelReason = res.result; cancelReason.value = res.result;
this.cancelAvail = true cancelAvail.value = true
this.cancelParams.reason = this.cancelReason[0].reason cancelParams.reason = cancelReason.value[0].reason
} }
}) })
}, }
sureCancel () { // 确定取消 function sureCancel () { // 确定取消
cancelOrder(this.cancelParams).then(res => { cancelOrder(cancelParams).then(res => {
if (res.success) { if (res.success) {
Message.success('取消订单成功') Message.success('取消订单成功')
this.getList() getList()
this.cancelAvail = false cancelAvail.value = false
} }
}) })
}, }
filterOrderStatus (status) { // 获取订单状态中文 function filterOrderStatus (status) { // 获取订单状态中文
const ob = this.orderStatusList.filter(e => { return e.status === status }); const ob = orderStatusList.filter(e => { return e.status === status });
return ob && ob[0] ? ob[0].name : status return ob && ob[0] ? ob[0].name : status
}, }
filterAfterSaleStatus (status) { function filterAfterSaleStatus (status) {
const ob = this.afterSaleStatusList.filter(e => e.status === status); const ob = afterSaleStatusList.filter(e => e.status === status);
return ob && ob[0] ? ob[0].name : status return ob && ob[0] ? ob[0].name : status
}, }
canAfterSale (order) { function canAfterSale (order) {
if (order.isAfterSale || order.orderType === 'E_COUPON' || !order.groupAfterSaleStatus) { if (order.isAfterSale || order.orderType === 'E_COUPON' || !order.groupAfterSaleStatus) {
return false return false
} }
return order.groupAfterSaleStatus.includes('NOT_APPLIED') || order.groupAfterSaleStatus.includes('PART_AFTER_SALE') return order.groupAfterSaleStatus.includes('NOT_APPLIED') || order.groupAfterSaleStatus.includes('PART_AFTER_SALE')
}, }
paymentLabel (order) { function paymentLabel (order) {
const map = { const map = {
WECHAT: '微信支付', WECHAT: '微信支付',
ALIPAY: '支付宝', ALIPAY: '支付宝',
@@ -454,9 +440,21 @@ export default {
return '待支付' return '待支付'
} }
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">

View File

@@ -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,89 +440,72 @@ 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: "", orderSn: "",
reason: "", reason: "",
}, });
cancelAvail: false, // 取消订单modal控制 const cancelAvail = ref(false); // 取消订单modal控制
cancelReason: [], // 取消订单原因 const cancelReason = ref([]); // 取消订单原因
orderPackage: [], const orderPackage = ref([]);
packageTraceList: [], const packageTraceList = ref([]);
logisticsModal: false, const logisticsModal = ref(false);
};
}, /** 电子卡券订单:无物流/核验码,卡密数据来自 API-B-01 orderItems[].cardKeys */
computed: { const isECouponOrder = computed(() => checkECouponOrder(order.value?.order?.orderType));
/** 电子卡券订单:无物流/核验码,卡密数据来自 API-B-01 orderItems[].cardKeys */ const isVirtualOrder = computed(() => order.value?.order?.orderType === "VIRTUAL");
isECouponOrder() { const isNonPhysicalOrder = computed(() => isVirtualOrder.value || isECouponOrder.value);
return checkECouponOrder(this.order?.order?.orderType); /** 是否展示卡密区块(仅当前订单 orderItems) */
}, const hasCardKeySection = computed(() =>
isVirtualOrder() { orderHasCardKeySection({
return this.order?.order?.orderType === "VIRTUAL"; orderItems: order.value.orderItems,
}, isECouponOrder: isECouponOrder.value,
isNonPhysicalOrder() { })
return this.isVirtualOrder || this.isECouponOrder; );
}, const cardKeyFulfillLines = computed(() => collectCardKeyFulfillLines(order.value.orderItems));
/** 是否展示卡密区块(仅当前订单 orderItems) */ const ecouponCardKeyRows = computed(() => flattenOrderCardKeys(order.value.orderItems, false));
hasCardKeySection() { /** 商家以无需配送方式发货(包裹 fulfillmentMode=NO_DELIVERY) */
return orderHasCardKeySection({ const isNoDeliveryOrder = computed(() => checkNoDeliveryOrder(orderPackage.value));
orderItems: this.order.orderItems, const displayDeliveryMethodValue = computed(() => {
isECouponOrder: this.isECouponOrder, if (isNoDeliveryOrder.value) return "无需配送";
}); return order.value?.deliveryMethodValue || "-";
}, });
cardKeyFulfillLines() { const noDeliveryPackages = computed(() => (orderPackage.value || []).filter(isNoDeliveryPackage));
return collectCardKeyFulfillLines(this.order.orderItems); const noDeliveryRemark = computed(() => {
}, const pkg = noDeliveryPackages.value.find((p) => p.deliveryRemark);
ecouponCardKeyRows() {
return flattenOrderCardKeys(this.order.orderItems, false);
},
/** 商家以无需配送方式发货(包裹 fulfillmentMode=NO_DELIVERY) */
isNoDeliveryOrder() {
return checkNoDeliveryOrder(this.orderPackage);
},
displayDeliveryMethodValue() {
if (this.isNoDeliveryOrder) return "无需配送";
return this.order?.deliveryMethodValue || "-";
},
noDeliveryPackages() {
return (this.orderPackage || []).filter(isNoDeliveryPackage);
},
noDeliveryRemark() {
const pkg = this.noDeliveryPackages.find((p) => p.deliveryRemark);
return pkg?.deliveryRemark || ""; return pkg?.deliveryRemark || "";
}, });
noDeliveryShipTime() { const noDeliveryShipTime = computed(() => {
const pkg = this.noDeliveryPackages[0]; const pkg = noDeliveryPackages.value[0];
return pkg?.logisticsTime || ""; return pkg?.logisticsTime || "";
}, });
},
methods: { function lineCardKeyRows(line) {
isNoDeliveryPackage,
getFulfillmentModeLabel,
cardKeyFulfillAlertType,
resolveCardKeyFulfillMessage,
lineCardKeyRows(line) {
return line.cardKeys || []; return line.cardKeys || [];
}, }
copyCardKey(row) {
function copyCardKey(row) {
const text = `卡号:${row.cardNo || ""}\n卡密:${row.cardSecret || ""}`; const text = `卡号:${row.cardNo || ""}\n卡密:${row.cardSecret || ""}`;
this.copyText(text); copyText(text);
}, }
copyAllCardKeys() {
const text = this.ecouponCardKeyRows function copyAllCardKeys() {
const text = ecouponCardKeyRows.value
.map((row, i) => `${i + 1}. 卡号:${row.cardNo || ""} 卡密:${row.cardSecret || ""}`) .map((row, i) => `${i + 1}. 卡号:${row.cardNo || ""} 卡密:${row.cardSecret || ""}`)
.join("\n"); .join("\n");
this.copyText(text || ""); copyText(text || "");
}, }
copyText(text) {
function copyText(text) {
if (!text) return; if (!text) return;
if (navigator.clipboard && navigator.clipboard.writeText) { if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(() => { navigator.clipboard.writeText(text).then(() => {
@@ -535,32 +520,37 @@ export default {
document.body.removeChild(ta); document.body.removeChild(ta);
Message.success("已复制到剪贴板"); Message.success("已复制到剪贴板");
} }
}, }
isVatSpecialReceipt (receipt) {
function isVatSpecialReceipt(receipt) {
if (!receipt) return false; if (!receipt) return false;
const rt = receipt.receiptType != null ? String(receipt.receiptType).trim() : ""; const rt = receipt.receiptType != null ? String(receipt.receiptType).trim() : "";
if (rt === "2" || rt === "增值税专用发票" || receipt.invoiceKind === "VAT_SPECIAL") { if (rt === "2" || rt === "增值税专用发票" || receipt.invoiceKind === "VAT_SPECIAL") {
return true; return true;
} }
return false; return false;
}, }
isPersonalReceipt (receipt) {
return !this.isVatSpecialReceipt(receipt); function isPersonalReceipt(receipt) {
}, return !isVatSpecialReceipt(receipt);
formatReceiptType (receipt) { }
function formatReceiptType(receipt) {
if (!receipt) return ""; if (!receipt) return "";
const rt = receipt.receiptType != null ? String(receipt.receiptType).trim() : ""; const rt = receipt.receiptType != null ? String(receipt.receiptType).trim() : "";
if (rt === "电子普通发票" || rt === "增值税专用发票") return rt; if (rt === "电子普通发票" || rt === "增值税专用发票") return rt;
return this.isVatSpecialReceipt(receipt) ? "增值税专用发票" : "电子普通发票"; return isVatSpecialReceipt(receipt) ? "增值税专用发票" : "电子普通发票";
}, }
formatReceiptHeaderType (receipt) {
function formatReceiptHeaderType(receipt) {
if (!receipt) return ""; if (!receipt) return "";
if (this.isVatSpecialReceipt(receipt)) return "单位"; if (isVatSpecialReceipt(receipt)) return "单位";
if (receipt.taxpayerId) return "单位"; if (receipt.taxpayerId) return "单位";
return "个人"; return "个人";
}, }
hasValidReceipt () {
const receipt = this.order && this.order.receipt; function hasValidReceipt() {
const receipt = order.value && order.value.receipt;
if (!receipt) return false; if (!receipt) return false;
const content = receipt.receiptContent ? String(receipt.receiptContent).trim() : ''; const content = receipt.receiptContent ? String(receipt.receiptContent).trim() : '';
const title = receipt.receiptTitle ? String(receipt.receiptTitle).trim() : ''; const title = receipt.receiptTitle ? String(receipt.receiptTitle).trim() : '';
@@ -568,39 +558,44 @@ export default {
// 后端有时会返回占位 receipt(如 receiptContent=不开发票),这里过滤掉 // 后端有时会返回占位 receipt(如 receiptContent=不开发票),这里过滤掉
if (content === '不开发票') return false; if (content === '不开发票') return false;
return Boolean(content || title || taxpayerId); return Boolean(content || title || taxpayerId);
}, }
getInvoiceAddress (receipt) {
function getInvoiceAddress(receipt) {
if (!receipt) return ""; if (!receipt) return "";
return receipt.invoiceAddress || receipt.invoiceFileUrl || ""; return receipt.invoiceAddress || receipt.invoiceFileUrl || "";
}, }
viewReceiptInvoice (receipt) {
const invoiceAddress = this.getInvoiceAddress(receipt); function viewReceiptInvoice(receipt) {
const invoiceAddress = getInvoiceAddress(receipt);
if (!invoiceAddress) { if (!invoiceAddress) {
Message.warning("暂无发票附件"); Message.warning("暂无发票附件");
return; return;
} }
window.open(invoiceAddress, "_blank"); window.open(invoiceAddress, "_blank");
}, }
goodsDetail(skuId, goodsId) {
function goodsDetail(skuId, goodsId) {
// 跳转商品详情 // 跳转商品详情
let routeUrl = this.$router.resolve({ let routeUrl = router.resolve({
path: "/goodsDetail", path: "/goodsDetail",
query: { skuId, goodsId }, query: { skuId, goodsId },
}); });
window.open(routeUrl.href, "_blank"); window.open(routeUrl.href, "_blank");
}, }
// 跳转店铺首页
shopPage(id) { // 跳转店铺首页
this.$router.push({ function shopPage(id) {
router.push({
path: "/merchant", path: "/merchant",
query: { id: id }, query: { id: id },
}); });
}, }
async getDetail() {
async function getDetail() {
// 获取订单详情 // 获取订单详情
orderDetail(this.$route.query.sn).then(async (res) => { orderDetail(route.query.sn).then(async (res) => {
if (res.success) { if (res.success) {
this.order = res.result; order.value = res.result;
const receiptId = const receiptId =
(res.result.order && res.result.order.receiptId) || (res.result.order && res.result.order.receiptId) ||
res.result.receiptId || res.result.receiptId ||
@@ -608,106 +603,115 @@ export default {
if (receiptId) { if (receiptId) {
const receiptRes = await receiptDetail(receiptId); const receiptRes = await receiptDetail(receiptId);
if (receiptRes && receiptRes.success && receiptRes.result) { if (receiptRes && receiptRes.success && receiptRes.result) {
this.order["receipt"] = receiptRes.result; order.value["receipt"] = receiptRes.result;
} }
} }
this.progressList = (res.result.orderLogs || []).slice().sort((a, b) => { progressList.value = (res.result.orderLogs || []).slice().sort((a, b) => {
return new Date(a.createTime).getTime() - new Date(b.createTime).getTime(); return new Date(a.createTime).getTime() - new Date(b.createTime).getTime();
}); });
if (this.order.order.deliveryMethod === 'LOGISTICS') { if (order.value.order.deliveryMethod === 'LOGISTICS') {
this.getOrderPackage(this.order.order.sn); getOrderPackage(order.value.order.sn);
} }
} }
}); });
}, }
getOrderPackage(sn) {
function getOrderPackage(sn) {
getPackage(sn).then(res => { getPackage(sn).then(res => {
if (res.success) { if (res.success) {
this.orderPackage = Array.isArray(res.result) ? res.result : []; orderPackage.value = Array.isArray(res.result) ? res.result : [];
if (!this.orderPackage.length) { if (!orderPackage.value.length) {
this.traces(); traces();
} }
} else { } else {
this.traces(); traces();
} }
}).catch(() => { }).catch(() => {
this.traces(); traces();
}); });
}, }
traces() {
function traces() {
// 物流信息 // 物流信息
getTraces(this.$route.query.sn).then((res) => { getTraces(route.query.sn).then((res) => {
if (res.success) { if (res.success) {
this.logistics = res.result; logistics.value = res.result;
} }
}); });
}, }
logisticsList() {
this.logisticsModal = true; function logisticsList() {
this.packageTraceList = this.orderPackage; logisticsModal.value = true;
packageTraceList.value = orderPackage.value;
// getTracesList(this.order.order.sn).then((res) => { // getTracesList(this.order.order.sn).then((res) => {
// if (res.success && res.result != null) { // if (res.success && res.result != null) {
// this.packageTraceList = res.result; // this.packageTraceList = res.result;
// } // }
// }); // });
}, }
received(sn) {
function received(sn) {
// 确认收货 // 确认收货
sureReceived(sn).then((res) => { sureReceived(sn).then((res) => {
if (res.success) { if (res.success) {
Message.success("确认收货成功"); Message.success("确认收货成功");
this.getDetail(); getDetail();
} }
}); });
}, }
goPay(sn) {
function goPay(sn) {
// 去支付 // 去支付
this.$router.push({ router.push({
path: "/payment", path: "/payment",
query: { orderType: "ORDER", sn }, query: { orderType: "ORDER", sn },
}); });
}, }
applyAfterSale(sn) {
function applyAfterSale(sn) {
// 申请售后 // 申请售后
this.$router.push({ name: "ApplyAfterSale", query: { sn: sn } }); router.push({ name: "ApplyAfterSale", query: { sn: sn } });
}, }
comment(sn, goodsIndex) {
function comment(sn, goodsIndex) {
// 评价 // 评价
this.$router.push({ router.push({
path: "/home/addEval", path: "/home/addEval",
query: { sn, index: goodsIndex }, query: { sn, index: goodsIndex },
}); });
}, }
complain(sn, goodsIndex) {
function complain(sn, goodsIndex) {
// 投诉 // 投诉
this.$router.push({ name: "Complain", query: { sn, index: goodsIndex } }); router.push({ name: "Complain", query: { sn, index: goodsIndex } });
}, }
handleCancelOrder(sn) {
function handleCancelOrder(sn) {
// 取消订单 // 取消订单
this.cancelParams.orderSn = sn; cancelParams.orderSn = sn;
afterSaleReason("CANCEL").then((res) => { afterSaleReason("CANCEL").then((res) => {
if (res.success) { if (res.success) {
this.cancelReason = res.result; cancelReason.value = res.result;
this.cancelAvail = true; cancelAvail.value = true;
this.cancelParams.reason = this.cancelReason[0].reason; cancelParams.reason = cancelReason.value[0].reason;
} }
}); });
}, }
sureCancel() {
function sureCancel() {
// 确定取消 // 确定取消
cancelOrder(this.cancelParams).then((res) => { cancelOrder(cancelParams).then((res) => {
if (res.success) { if (res.success) {
Message.success("取消订单成功"); Message.success("取消订单成功");
this.getDetail(); getDetail();
this.cancelAvail = false; cancelAvail.value = false;
} }
}); });
}, }
},
mounted() { onMounted(() => {
this.getDetail(); getDetail();
}, });
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.mb_10 { .mb_10 {

View File

@@ -50,85 +50,87 @@
</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", defineOptions({ name: "Coupons" });
data() {
return { const router = useRouter();
statusNameList: [
const statusNameList = ref([
// 优惠券状态 // 优惠券状态
"未使用", "未使用",
"已使用", "已使用",
"已过期", "已过期",
], ]);
statusList: ["NEW", "USED", "EXPIRE"], // 优惠券状态 const statusList = ref(["NEW", "USED", "EXPIRE"]); // 优惠券状态
loading: false, // 列表加载状态 const loading = ref(false); // 列表加载状态
params: { const params = reactive({
// 请求参数 // 请求参数
pageNumber: 1, pageNumber: 1,
pageSize: 20, pageSize: 20,
memberCouponStatus: "NEW", memberCouponStatus: "NEW",
}, });
total: 0, // 优惠券总数 const total = ref(0); // 优惠券总数
list: [], // 优惠券列表 const list = ref([]); // 优惠券列表
};
}, const activeStatusIndex = computed(() => {
computed: { const index = statusList.value.indexOf(params.memberCouponStatus);
activeStatusIndex() {
const index = this.statusList.indexOf(this.params.memberCouponStatus);
return index > -1 ? index : 0; return index > -1 ? index : 0;
}, });
},
methods: { // 获取优惠券列表
getList() { function getList() {
// 获取优惠券列表 loading.value = true;
this.loading = true; memberCouponList(params)
memberCouponList(this.params)
.then((res) => { .then((res) => {
this.loading = false; loading.value = false;
if (res.success) { if (res.success) {
this.list = res.result.records || []; list.value = res.result.records || [];
this.total = res.result.total || 0; total.value = res.result.total || 0;
} }
}) })
.catch(() => { .catch(() => {
this.loading = false; loading.value = false;
this.list = []; list.value = [];
this.total = 0; total.value = 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({ // 切换优惠券状态
function change(index) {
params.memberCouponStatus = statusList.value[index];
params.pageNumber = 1;
getList();
}
// 根据使用条件跳转商品列表页面
function go(item) {
if (params.memberCouponStatus !== "NEW") return;
router.push({
path: "/goodsList", path: "/goodsList",
query: { promotionsId: item.couponId, promotionType: "COUPON" }, query: { promotionsId: item.couponId, promotionType: "COUPON" },
}); });
}, }
changePageNum(val) { // 分页改变页码
// 分页改变页码 function changePageNum(val) {
this.params.pageNumber = val; params.pageNumber = val;
this.getList(); getList();
}, }
changePageSize(val) { // 分页改变页数
// 分页改变页数 function changePageSize(val) {
this.params.pageNumber = 1; params.pageNumber = 1;
this.params.pageSize = val; params.pageSize = val;
this.getList(); getList();
}, }
useScope(type, storeName) { // 根据字段返回 优惠券适用范围
// 根据字段返回 优惠券适用范围 function useScope(type, storeName) {
let shop = "平台"; let shop = "平台";
let goods = "全部商品"; let goods = "全部商品";
if (storeName !== "platform") shop = storeName; if (storeName !== "platform") shop = storeName;
@@ -144,16 +146,18 @@ export default {
break; break;
} }
return `${shop}${goods}可用`; return `${shop}${goods}可用`;
}, }
formatValidity(item) {
const start = this.formatCouponTime(item && item.startTime); function formatValidity(item) {
const end = this.formatCouponTime(item && item.endTime); const start = formatCouponTime(item && item.startTime);
const end = formatCouponTime(item && item.endTime);
if (start && end) return `${start} 至 ${end}`; if (start && end) return `${start} 至 ${end}`;
if (end) return `至 ${end}`; if (end) return `至 ${end}`;
if (start) return `${start} 起`; if (start) return `${start} 起`;
return "长期有效"; return "长期有效";
}, }
formatCouponTime(value) {
function formatCouponTime(value) {
if (!value) return ""; if (!value) return "";
const text = String(value).trim(); const text = String(value).trim();
if (/^\d{4}-\d{2}-\d{2}/.test(text)) { if (/^\d{4}-\d{2}-\d{2}/.test(text)) {
@@ -163,12 +167,11 @@ export default {
if (Number.isNaN(date.getTime())) return text; if (Number.isNaN(date.getTime())) return text;
const pad = (n) => String(n).padStart(2, "0"); const pad = (n) => String(n).padStart(2, "0");
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`; return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`;
}, }
},
mounted() { onMounted(() => {
this.getList(); getList();
}, });
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -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,55 +180,59 @@
</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);
const activeWalletTab = ref("log");
const frozenDeposit = ref(0); // 冻结余额
const memberDeposit = ref(0); // 余额
const modal = ref(false); // 余额充值
const withdrawApplyModal = ref(false); // 提现申请
const formData = reactive({
// 充值金额 // 充值金额
price: 1 price: 1
}, });
// 提现金额 // 提现金额
withdrawApplyFormData: { const withdrawApplyFormData = reactive({
price: 1, price: 1,
minPrice: 1, minPrice: 1,
type: '', type: '',
realName: '', realName: '',
connectNumber: '', connectNumber: '',
}, });
// 余额日志 // 余额日志
walletForm: { const walletForm = reactive({
// 搜索框初始化对象 // 搜索框初始化对象
pageNumber: 1, pageNumber: 1,
pageSize: 20 pageSize: 20
}, });
// 充值记录 // 充值记录
rechargeForm: { const rechargeForm = reactive({
// 搜索框初始化对象 // 搜索框初始化对象
pageNumber: 1, // 当前页数 pageNumber: 1, // 当前页数
pageSize: 20 // 页面大小 pageSize: 20 // 页面大小
}, });
// 提现记录 // 提现记录
withdrawApplyForm: { const withdrawApplyForm = reactive({
// 搜索框初始化对象 // 搜索框初始化对象
pageNumber: 1, // 当前页数 pageNumber: 1, // 当前页数
pageSize: 20 // 页面大小 pageSize: 20 // 页面大小
}, });
// 提现申请校验 // 提现申请校验
withdrawApplyFormValidate: { const withdrawApplyFormValidate = {
price: [ price: [
{required: true, message: '请输入大于0小于9999的合法提现金额'}, {required: true, message: '请输入大于0小于9999的合法提现金额'},
{ {
@@ -243,8 +247,8 @@ export default {
connectNumber: [ connectNumber: [
{required: true, message: '请输入第三方登录账号'}, {required: true, message: '请输入第三方登录账号'},
], ],
}, };
formValidate: { const formValidate = {
price: [ price: [
{required: true, message: '请输入大于等于1小于9999的合法充值金额'}, {required: true, message: '请输入大于等于1小于9999的合法充值金额'},
{ {
@@ -253,65 +257,64 @@ export default {
trigger: 'change' trigger: 'change'
} }
] ]
}, };
logColumnsData: {}, const logColumnsData = ref({});
rechargeListData: {}, const rechargeListData = ref({});
withdrawApplyColumnsListData: {} const withdrawApplyColumnsListData = ref({});
};
}, onMounted(() => {
mounted() { init();
this.init(); });
},
methods: { // 初始化数据
// 初始化数据 function init() {
init() {
getMembersWallet().then((res) => { getMembersWallet().then((res) => {
this.frozenDeposit = res.result.memberFrozenWallet; frozenDeposit.value = res.result.memberFrozenWallet;
this.memberDeposit = res.result.memberWallet; memberDeposit.value = res.result.memberWallet;
}); });
getDepositLog(this.walletForm).then((res) => { getDepositLog(walletForm).then((res) => {
if (res.message === 'success') { if (res.message === 'success') {
this.logColumnsData = res.result; logColumnsData.value = res.result;
} }
}); });
}, }
tabPaneChange(v) { function tabPaneChange(v) {
this.activeWalletTab = v; activeWalletTab.value = v;
if (v === 'recharge') { if (v === 'recharge') {
this.getRechargeData(); getRechargeData();
} }
if (v === 'log') { if (v === 'log') {
this.init(); init();
} }
if (v === 'withdrawApply') { if (v === 'withdrawApply') {
this.getWithdrawApplyData(); getWithdrawApplyData();
} }
}, }
// 充值记录 // 充值记录
getRechargeData() { function getRechargeData() {
getRecharge(this.rechargeForm).then((res) => { getRecharge(rechargeForm).then((res) => {
if (res.message === 'success') { if (res.message === 'success') {
this.rechargeListData = res.result; rechargeListData.value = res.result;
} }
}); });
}, }
// 提现记录 // 提现记录
getWithdrawApplyData() { function getWithdrawApplyData() {
getWithdrawApply(this.withdrawApplyForm).then((res) => { getWithdrawApply(withdrawApplyForm).then((res) => {
if (res.message === 'success') { if (res.message === 'success') {
this.withdrawApplyColumnsListData = res.result; withdrawApplyColumnsListData.value = res.result;
} }
}); });
}, }
rechargeStatusText (status) { function rechargeStatusText (status) {
const map = { PAID: '已付款', UNPAID: '未付款', CANCEL: '已取消' } const map = { PAID: '已付款', UNPAID: '未付款', CANCEL: '已取消' }
return map[status] || status || '—' return map[status] || status || '—'
}, }
rechargeWayText (way) { function rechargeWayText (way) {
const map = { ALIPAY: '支付宝', WECHAT: '微信', BANK_TRANSFER: '线下转账' } const map = { ALIPAY: '支付宝', WECHAT: '微信', BANK_TRANSFER: '线下转账' }
return map[way] || way || '—' return map[way] || way || '—'
}, }
withdrawStatusText (status) { function withdrawStatusText (status) {
const map = { const map = {
APPLY: '申请中', APPLY: '申请中',
VIA_AUDITING: '审核通过', VIA_AUDITING: '审核通过',
@@ -319,49 +322,49 @@ export default {
ERROR: '提现失败' ERROR: '提现失败'
} }
return map[status] || '审核拒绝' return map[status] || '审核拒绝'
}, }
// 余额日志 // 余额日志
changePage(v) { function changePage(v) {
this.walletForm.pageNumber = v; walletForm.pageNumber = v;
this.init(); init();
}, }
changePageSize(v) { function changePageSize(v) {
this.walletForm.pageNumber = 1; walletForm.pageNumber = 1;
this.walletForm.pageSize = v; walletForm.pageSize = v;
this.init(); init();
}, }
// 充值记录 // 充值记录
rechargeChangePage(v) { function rechargeChangePage(v) {
this.rechargeForm.pageNumber = v; rechargeForm.pageNumber = v;
this.getRechargeData(); getRechargeData();
}, }
rechargeChangePageSize(v) { function rechargeChangePageSize(v) {
this.rechargeForm.pageNumber = 1; rechargeForm.pageNumber = 1;
this.rechargeForm.pageSize = v; rechargeForm.pageSize = v;
this.getRechargeData(); getRechargeData();
}, }
// 提现记录 // 提现记录
withdrawChangePage(v) { function withdrawChangePage(v) {
this.withdrawApplyForm.pageNumber = v; withdrawApplyForm.pageNumber = v;
this.getWithdrawApplyData(); getWithdrawApplyData();
}, }
withdrawChangePageSize(v) { function withdrawChangePageSize(v) {
this.withdrawApplyForm.pageNumber = 1; withdrawApplyForm.pageNumber = 1;
this.withdrawApplyForm.pageSize = v; withdrawApplyForm.pageSize = v;
this.getWithdrawApplyData(); getWithdrawApplyData();
}, }
// 弹出在线充值框 // 弹出在线充值框
recharge() { function recharge() {
this.formData.price = 1; formData.price = 1;
this.modal = true; modal.value = true;
}, }
// 在线充值 // 在线充值
rechargePrice() { function rechargePrice() {
this.$refs['formData'].validate((valid) => { formDataRef.value.validate((valid) => {
if (valid) { if (valid) {
recharge(this.formData).then((res) => { apiRecharge(formData).then((res) => {
if (res.message === 'success') { if (res.message === 'success') {
this.$router.push({ router.push({
path: '/payment', path: '/payment',
query: {orderType: 'RECHARGE', sn: res.result.rechargeSn} query: {orderType: 'RECHARGE', sn: res.result.rechargeSn}
}); });
@@ -369,40 +372,38 @@ export default {
}); });
} }
}); });
}, }
// 申请提现弹出框 // 申请提现弹出框
withdrawalApply() { function withdrawalApply() {
this.withdrawApplyModal = true; withdrawApplyModal.value = true;
this.withdrawApplyFormData.minPrice = 1; withdrawApplyFormData.minPrice = 1;
this.withdrawApplyFormData.price = 1; withdrawApplyFormData.price = 1;
this.withdrawApplyFormData.type = ''; withdrawApplyFormData.type = '';
this.withdrawApplyFormData.realName = ''; withdrawApplyFormData.realName = '';
this.withdrawApplyFormData.connectNumber = ''; withdrawApplyFormData.connectNumber = '';
withdrawalSettingVO().then((res) => { withdrawalSettingVO().then((res) => {
if (res.code === 200) { if (res.code === 200) {
this.withdrawApplyFormData.minPrice = res.result.minPrice; withdrawApplyFormData.minPrice = res.result.minPrice;
this.withdrawApplyFormData.type = res.result.type; withdrawApplyFormData.type = res.result.type;
this.withdrawApplyFormData.price = 1; withdrawApplyFormData.price = 1;
} }
}); });
}, }
// 提现 // 提现
withdrawal() { function withdrawal() {
this.$refs['withdrawApplyFormData'].validate((valid) => { withdrawApplyFormDataRef.value.validate((valid) => {
if (valid) { if (valid) {
withdrawalApply(this.withdrawApplyFormData).then((res) => { apiWithdrawalApply(withdrawApplyFormData).then((res) => {
if (res && res.success) { if (res && res.success) {
Message.success('提现申请成功,关注提现状态'); Message.success('提现申请成功,关注提现状态');
this.withdrawApplyModal = false; withdrawApplyModal.value = false;
this.init(); // 余额查询 init(); // 余额查询
this.getWithdrawApplyData(); // 提现记录 getWithdrawApplyData(); // 提现记录
} }
}); });
} }
}); });
} }
}
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@@ -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,57 +102,56 @@ 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, const noticeModal = ref(false);
serviceHotline: "12221111", const serviceHotline = ref("12221111");
activatingId: "", const activatingId = ref("");
}; const bindFormRef = ref(null);
},
computed: { const primaryActionLabel = computed(() => {
primaryActionLabel() { if (statusTab.value === "PENDING_ACTIVATION") {
if (this.statusTab === "PENDING_ACTIVATION") {
return "激活自用"; return "激活自用";
} }
if (this.statusTab === "AVAILABLE") { if (statusTab.value === "AVAILABLE") {
return "去使用"; return "去使用";
} }
return "不可用"; return "不可用";
}, });
primaryActionDisabled() { const primaryActionDisabled = computed(() => {
return this.statusTab === "UNAVAILABLE"; return statusTab.value === "UNAVAILABLE";
}, });
pendingActivateLocked() { const pendingActivateLocked = computed(() => {
return this.statusTab === "PENDING_ACTIVATION" && !!this.activatingId; return statusTab.value === "PENDING_ACTIVATION" && !!activatingId.value;
}, });
},
mounted() { onMounted(() => {
this.getList(); getList();
}, });
methods: {
formatYuan(val) { function formatYuan(val) {
if (val == null || val === "") { if (val == null || val === "") {
return "—"; return "—";
} }
@@ -159,8 +160,9 @@ export default {
return String(val); return String(val);
} }
return Number.isInteger(n) ? String(n) : n.toFixed(2).replace(/\.?0+$/, ""); return Number.isInteger(n) ? String(n) : n.toFixed(2).replace(/\.?0+$/, "");
}, }
formatValidity(row) {
function formatValidity(row) {
const t = row && row.expireTime; const t = row && row.expireTime;
if (!t) { if (!t) {
return "长期有效"; return "长期有效";
@@ -176,103 +178,111 @@ export default {
const m = String(d.getMonth() + 1).padStart(2, "0"); const m = String(d.getMonth() + 1).padStart(2, "0");
const day = String(d.getDate()).padStart(2, "0"); const day = String(d.getDate()).padStart(2, "0");
return `${y}-${m}-${day} 到期`; return `${y}-${m}-${day} 到期`;
}, }
onStatusTab(name) {
this.statusTab = name; function onStatusTab(name) {
this.params.memberCardStatus = name; statusTab.value = name;
this.params.pageNumber = 1; params.memberCardStatus = name;
this.getList(); params.pageNumber = 1;
}, getList();
getList() { }
this.loading = true;
const p = { ...this.params }; function getList() {
loading.value = true;
const p = { ...params };
if (p.memberCardStatus) { if (p.memberCardStatus) {
p.memberCardStatus = String(p.memberCardStatus); p.memberCardStatus = String(p.memberCardStatus);
} }
getGiftCardCashMemberCardPage(p) getGiftCardCashMemberCardPage(p)
.then((res) => { .then((res) => {
this.loading = false; loading.value = false;
if (res.success && res.result) { if (res.success && res.result) {
this.list = res.result.records || []; list.value = res.result.records || [];
this.total = res.result.total || 0; total.value = res.result.total || 0;
} }
}) })
.catch(() => { .catch(() => {
this.loading = false; loading.value = false;
}); });
}, }
changePage(val) {
this.params.pageNumber = val; function changePage(val) {
this.getList(); params.pageNumber = val;
}, getList();
changePageSize(val) { }
this.params.pageNumber = 1;
this.params.pageSize = val; function changePageSize(val) {
this.getList(); params.pageNumber = 1;
}, params.pageSize = val;
resetBindForm() { getList();
this.bindForm = { cardNo: "", cardSecret: "" }; }
this.$nextTick(() => {
if (this.$refs.bindForm) { function resetBindForm() {
this.$refs.bindForm.resetFields(); bindForm.value = { cardNo: "", cardSecret: "" };
nextTick(() => {
if (bindFormRef.value) {
bindFormRef.value.resetFields();
} }
}); });
}, }
submitBind() {
this.$refs.bindForm.validate((valid) => { function submitBind() {
bindFormRef.value.validate((valid) => {
if (!valid) { if (!valid) {
return; return;
} }
this.bindSubmitting = true; bindSubmitting.value = true;
bindGiftCardCashMemberCard({ bindGiftCardCashMemberCard({
cardNo: String(this.bindForm.cardNo).trim(), cardNo: String(bindForm.value.cardNo).trim(),
cardSecret: String(this.bindForm.cardSecret).trim(), cardSecret: String(bindForm.value.cardSecret).trim(),
}) })
.then((res) => { .then((res) => {
this.bindSubmitting = false; bindSubmitting.value = false;
if (res.success) { if (res.success) {
Message.success(res.message || "兑换成功"); Message.success(res.message || "兑换成功");
this.resetBindForm(); resetBindForm();
this.params.pageNumber = 1; params.pageNumber = 1;
this.getList(); getList();
} }
}) })
.catch(() => { .catch(() => {
this.bindSubmitting = false; bindSubmitting.value = false;
}); });
}); });
}, }
onCardPrimaryAction(item) {
if (this.primaryActionDisabled) { function onCardPrimaryAction(item) {
if (primaryActionDisabled.value) {
return; return;
} }
if (this.statusTab === "PENDING_ACTIVATION") { if (statusTab.value === "PENDING_ACTIVATION") {
const memberCardId = this.resolveMemberCardId(item); const memberCardId = resolveMemberCardId(item);
if (!memberCardId) { if (!memberCardId) {
Message.error("卡片信息异常,无法激活"); Message.error("卡片信息异常,无法激活");
return; return;
} }
this.activatingId = memberCardId; activatingId.value = memberCardId;
activateGiftCardCashMemberCard(memberCardId) activateGiftCardCashMemberCard(memberCardId)
.then((res) => { .then((res) => {
if (res.success) { if (res.success) {
Message.success(res.message || "激活成功"); Message.success(res.message || "激活成功");
this.getList(); getList();
} }
}) })
.catch((err) => { .catch((err) => {
console.warn('激活礼品卡失败', err); console.warn('激活礼品卡失败', err);
}) })
.finally(() => { .finally(() => {
this.activatingId = ""; activatingId.value = "";
}); });
return; return;
} }
if (this.statusTab === "AVAILABLE") { if (statusTab.value === "AVAILABLE") {
this.$router.push({ path: "/goodsList" }); router.push({ path: "/goodsList" });
} }
}, }
resolveMemberCardId(row) {
function resolveMemberCardId(row) {
if (!row) { if (!row) {
return ""; return "";
} }
@@ -281,13 +291,12 @@ export default {
: row.id != null && row.id !== "" : row.id != null && row.id !== ""
? String(row.id) ? String(row.id)
: ""; : "";
}, }
isActivating(item) {
const id = this.resolveMemberCardId(item); function isActivating(item) {
return !!this.activatingId && this.activatingId === id; const id = resolveMemberCardId(item);
}, return !!activatingId.value && activatingId.value === id;
}, }
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -38,46 +38,45 @@
</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();
},
methods: {
goodsDetail(skuId, goodsId) {
// 跳转商品详情 // 跳转商品详情
let routeUrl = this.$router.resolve({ let routeUrl = router.resolve({
path: "/goodsDetail", path: "/goodsDetail",
query: { skuId, goodsId }, query: { skuId, goodsId },
}); });
window.open(routeUrl.href, "_blank"); window.open(routeUrl.href, "_blank");
}, }
// 跳转店铺首页
shopPage(id) { // 跳转店铺首页
this.$router.push({ function shopPage(id) {
router.push({
path: "/merchant", path: "/merchant",
query: { id: id }, query: { id: id },
}); });
}, }
clearAll() {
function clearAll() {
// 清除全部足迹 // 清除全部足迹
Modal.confirm({ Modal.confirm({
title: "删除", title: "删除",
@@ -86,50 +85,56 @@ export default {
clearTracks().then((res) => { clearTracks().then((res) => {
if (res.success) { if (res.success) {
Message.success("删除成功"); Message.success("删除成功");
this.getList(); getList();
} }
}); });
}, },
onCancel: () => {}, onCancel: () => {},
}); });
}, }
clearById(id) {
function clearById(id) {
// 清除全部足迹 // 清除全部足迹
clearTracksById(id).then((res) => { clearTracksById(id).then((res) => {
if (res.success) { if (res.success) {
Message.success("删除成功"); Message.success("删除成功");
this.getList(); getList();
} }
}); });
}, }
changePageNum(val) {
function changePageNum(val) {
// 修改页码 // 修改页码
this.params.pageNumber = val; params.pageNumber = val;
this.getList(); getList();
}, }
changePageSize(val) {
function changePageSize(val) {
// 修改页数 // 修改页数
this.params.pageNumber = 1; params.pageNumber = 1;
this.params.pageSize = val; params.pageSize = val;
this.getList(); getList();
}, }
getList() {
function getList() {
// 获取足迹列表 // 获取足迹列表
this.spinShow = true; spinShow.value = true;
tracksList(this.params).then((res) => { tracksList(params).then((res) => {
this.spinShow = false; spinShow.value = false;
if (res.success && res.result.records.length) { if (res.success && res.result.records.length) {
this.list = res.result.records.filter(item =>{ list.value = res.result.records.filter(item =>{
return item != null return item != null
}); });
this.total = res.result.total total.value = res.result.total
} else { } else {
this.list = []; list.value = [];
} }
}); });
}, }
},
}; onMounted(() => {
getList();
});
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.wrapper { .wrapper {

View File

@@ -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> <div>结算页</div>
</div> </div>
<div class="cart-steps"> <div class="cart-steps">
@@ -317,7 +317,9 @@
</div> </div>
</template> </template>
<script> <script setup>
import { computed, getCurrentInstance, nextTick, onMounted, reactive, ref } from "vue";
import { useRoute, useRouter } from "vue-router";
import { Message, Modal, Spin } from "@/utils/message"; import { Message, Modal, Spin } from "@/utils/message";
import { ArrowDown, ArrowUp, Check, Help } from '@element-plus/icons-vue'; import { ArrowDown, ArrowUp, Check, Help } from '@element-plus/icons-vue';
import invoiceModal from "@/components/invoiceModal"; import invoiceModal from "@/components/invoiceModal";
@@ -337,36 +339,96 @@ import {
import { getStoreAddress } from "@/api/shopentry.js" import { getStoreAddress } from "@/api/shopentry.js"
import { canUseCouponList } from "@/api/member.js"; import { canUseCouponList } from "@/api/member.js";
import { isECoupon, shouldSkipPaymentPage } from "@/constants/goodsType"; import { isECoupon, shouldSkipPaymentPage } from "@/constants/goodsType";
import { useAppStore } from "@/stores/app";
export default { defineOptions({ name: "Pay" });
name: "Pay",
components: { invoiceModal, addressManage }, const route = useRoute();
computed: { const router = useRouter();
hasInvoiceInfo() { const appStore = useAppStore();
const instance = getCurrentInstance();
const logoImg = computed(() => appStore.logoImg);
const invModal = ref(null);
const address = ref(null);
const selectedStoreAddress = ref('m');
const selectMethod = ref('');
const stepIndex = ref(1); // 顶部步骤条状态
const invoiceAvailable = ref(false); // 发票编辑按钮
const showEditBtn = ref(""); // 鼠标移入显示编辑按钮
const orderMark = ref(""); // 订单备注
const remarkEditing = ref({}); // 正在填写备注的店铺
const goodsType = ref(""); // 商品类型
const storeMoreAddr = ref(false);
const invoiceData = ref({
// 发票数据
//receiptContent: "不开发票",
});
const searchForm = reactive({
pageNumber: 1,
pageSize: 100
});
const shippingMethod = ref([]);
const storeAddressList = ref([]);
const shippingWay = [
{
value: "LOGISTICS",
label: "物流",
},
{
value: "SELF_PICK_UP",
label: "自提",
},
];
const selectedDeliverMethod = ref('LOGISTICS');
const addressList = ref([]); // 地址列表
const selectedAddress = ref({}); // 所选地址
const goodsList = ref([]); // 商品列表
const skuList = ref([]); // 商品sku列表
const priceDetailDTO = ref({}); // 商品价格
const totalNum = ref(0); // 购买数量
const addrId = ref(""); // 编辑地址传入的id
const moreAddr = ref(false); // 更多地址
const canUseCouponNum = ref(0); // 可用优惠券数量
const couponList = ref([]); // 可用优惠券列表
const usedCouponId = ref([]); // 已使用优惠券id
const selectedCoupon = ref({}); // 已选优惠券对象
const storeId = ref(''); //店铺Id
const giftCardList = ref([]); // TradeDTO.canUseGiftCards
const giftCardNoticeModal = ref(false);
const giftCardDeductItems = ref([]); // 已选礼品卡抵扣明细(TradeDTO.giftCardDeductItems)
const tradeGiftCardDeductTotal = ref(null); // TradeDTO.giftCardDeductTotal
const selectedGiftCardCredentialIds = ref([]); // 已选凭证 id,与 deductItems 同步
// 各店铺备注输入框组件实例,key 为 remarkKey(shop)
const remarkInputRefs = {};
const hasInvoiceInfo = computed(() => {
const title = const title =
this.invoiceData.personalName || invoiceData.value.personalName ||
this.invoiceData.companyName || invoiceData.value.companyName ||
this.invoiceData.receiptTitle; invoiceData.value.receiptTitle;
const content = this.invoiceData.receiptContent; const content = invoiceData.value.receiptContent;
if (content && String(content).trim() === "不开发票") return false; if (content && String(content).trim() === "不开发票") return false;
return Boolean( return Boolean(
(title && String(title).trim()) || (title && String(title).trim()) ||
(content && String(content).trim()) (content && String(content).trim())
); );
}, });
invoiceDisplayTitle() { const invoiceDisplayTitle = computed(() => {
return ( return (
this.invoiceData.personalName || invoiceData.value.personalName ||
this.invoiceData.companyName || invoiceData.value.companyName ||
this.invoiceData.receiptTitle || invoiceData.value.receiptTitle ||
"" ""
); );
}, });
giftCardDeductAmount() { const giftCardDeductAmount = computed(() => {
if (this.tradeGiftCardDeductTotal != null && Number(this.tradeGiftCardDeductTotal) > 0) { if (tradeGiftCardDeductTotal.value != null && Number(tradeGiftCardDeductTotal.value) > 0) {
return Number(this.tradeGiftCardDeductTotal); return Number(tradeGiftCardDeductTotal.value);
} }
const items = this.giftCardDeductItems || []; const items = giftCardDeductItems.value || [];
let sum = 0; let sum = 0;
items.forEach((it) => { items.forEach((it) => {
sum += Number(it.deductAmount || 0); sum += Number(it.deductAmount || 0);
@@ -374,7 +436,7 @@ export default {
if (sum > 0) { if (sum > 0) {
return sum; return sum;
} }
const p = this.priceDetailDTO || {}; const p = priceDetailDTO.value || {};
const n = Number( const n = Number(
p.giftCardPrice != null p.giftCardPrice != null
? p.giftCardPrice ? p.giftCardPrice
@@ -385,88 +447,37 @@ export default {
: 0 : 0
); );
return Number.isFinite(n) ? n : 0; return Number.isFinite(n) ? n : 0;
}, });
/** 电子卡券结算:免地址、隐藏发票;优惠券/礼品卡/促销明细可用(M-02) */ /** 电子卡券结算:免地址、隐藏发票;优惠券/礼品卡/促销明细可用(M-02) */
isECouponCheckout() { const isECouponCheckout = computed(() => {
return isECoupon(this.goodsType); return isECoupon(goodsType.value);
}, });
/** 虚拟型结算(核销虚拟 + 电子卡券):均不需要收货地址 */ /** 虚拟型结算(核销虚拟 + 电子卡券):均不需要收货地址 */
isVirtualLikeCheckout() { const isVirtualLikeCheckout = computed(() => {
return this.goodsType === "VIRTUAL_GOODS" || this.isECouponCheckout; return goodsType.value === "VIRTUAL_GOODS" || isECouponCheckout.value;
}, });
},
data() { onMounted(() => {
return { init();
selectedStoreAddress: 'm', });
selectMethod: '',
stepIndex: 1, // 顶部步骤条状态 // 初始化数据
invoiceAvailable: false, // 发票编辑按钮 function init() {
showEditBtn: "", // 鼠标移入显示编辑按钮 getGoodsDetail();
orderMark: "", // 订单备注 getDistribution();
remarkEditing: {}, // 正在填写备注的店铺 }
goodsType: "", // 商品类型 function goAddressManage() {
storeMoreAddr: false,
invoiceData: {
// 发票数据
//receiptContent: "不开发票",
},
searchForm: {
pageNumber: 1,
pageSize: 100
},
shippingMethod: [],
storeAddressList: [],
shippingWay: [
{
value: "LOGISTICS",
label: "物流",
},
{
value: "SELF_PICK_UP",
label: "自提",
},
],
selectedDeliverMethod: 'LOGISTICS',
addressList: [], // 地址列表
selectedAddress: {}, // 所选地址
goodsList: [], // 商品列表
priceDetailDTO: {}, // 商品价格
totalNum: 0, // 购买数量
addrId: "", // 编辑地址传入的id
moreAddr: false, // 更多地址
canUseCouponNum: 0, // 可用优惠券数量
couponList: [], // 可用优惠券列表
usedCouponId: [], // 已使用优惠券id
selectedCoupon: {}, // 已选优惠券对象
storeId: '', //店铺Id
giftCardList: [], // TradeDTO.canUseGiftCards
giftCardNoticeModal: false,
giftCardDeductItems: [], // 已选礼品卡抵扣明细(TradeDTO.giftCardDeductItems)
tradeGiftCardDeductTotal: null, // TradeDTO.giftCardDeductTotal
selectedGiftCardCredentialIds: [], // 已选凭证 id,与 deductItems 同步
};
},
mounted() {
this.init();
},
methods: {
// 初始化数据
init() {
this.getGoodsDetail();
this.getDistribution();
},
goAddressManage() {
// 跳转地址管理页面 // 跳转地址管理页面
this.$router.push("/home/MyAddress"); router.push("/home/MyAddress");
}, }
getAddress() { function getAddress() {
// 获取收货地址列表 // 获取收货地址列表
memberAddress(this.searchForm).then((res) => { memberAddress(searchForm).then((res) => {
if (res.success) { if (res.success) {
this.addressList = res.result?.records || []; addressList.value = res.result?.records || [];
this.addressList.forEach((e, index) => { addressList.value.forEach((e, index) => {
if (e.id === this.selectedAddress.id && index > 2) { if (e.id === selectedAddress.value.id && index > 2) {
this.moreAddr = true; moreAddr.value = true;
} }
}); });
} }
@@ -474,37 +485,37 @@ export default {
// 网络/登录异常已由 request 层统一提示,地址列表保持为空,可正常新增地址 // 网络/登录异常已由 request 层统一提示,地址列表保持为空,可正常新增地址
console.warn("获取收货地址列表失败", err); console.warn("获取收货地址列表失败", err);
}); });
}, }
// 获取配送方式列表 // 获取配送方式列表
async getDistribution() { async function getDistribution() {
try { try {
let shopRes = await shippingMethodList({ way: this.$route.query.way }); let shopRes = await shippingMethodList({ way: route.query.way });
let shopList; let shopList;
if (shopRes.success) { if (shopRes.success) {
shopList = shopRes.result || []; shopList = shopRes.result || [];
let way = []; let way = [];
this.shippingWay.forEach((item) => { shippingWay.forEach((item) => {
shopList.forEach((child) => { shopList.forEach((child) => {
if (item.value == child) { if (item.value == child) {
way.push(item); way.push(item);
} }
}); });
}); });
this.shippingMethod = way; shippingMethod.value = way;
} }
} catch (err) { } catch (err) {
// 网络/登录异常已由 request 层统一提示,配送方式保持为空不影响页面其余模块 // 网络/登录异常已由 request 层统一提示,配送方式保持为空不影响页面其余模块
console.warn("获取配送方式列表失败", err); console.warn("获取配送方式列表失败", err);
} }
}, }
async getStoreAddressList() { async function getStoreAddressList() {
getStoreAddress(this.storeId,this.searchForm).then(res => { getStoreAddress(storeId.value, searchForm).then(res => {
if (res.success) { if (res.success) {
this.storeAddressList = res.result?.records || [] storeAddressList.value = res.result?.records || []
this.storeAddressList.forEach((e, index) => { storeAddressList.value.forEach((e, index) => {
if (e.id === this.selectedAddress.id && index > 2) { if (e.id === selectedAddress.value.id && index > 2) {
this.storeMoreAddr = true; storeMoreAddr.value = true;
} }
}); });
} }
@@ -512,11 +523,11 @@ export default {
// 网络/登录异常已由 request 层统一提示,自提点列表保持为空 // 网络/登录异常已由 request 层统一提示,自提点列表保持为空
console.warn("获取自提点列表失败", err); console.warn("获取自提点列表失败", err);
}) })
}, }
getGoodsDetail() { function getGoodsDetail() {
// 订单商品详情 // 订单商品详情
Spin.show(); Spin.show();
cartGoodsPay({ way: this.$route.query.way }) cartGoodsPay({ way: route.query.way })
.then((res) => { .then((res) => {
Spin.hide(); Spin.hide();
if (res.success) { if (res.success) {
@@ -536,27 +547,27 @@ export default {
(firstSku.errorMessage || ""), (firstSku.errorMessage || ""),
}); });
} }
this.$router.push({ router.push({
path: "/cart", path: "/cart",
replace: true, replace: true,
}); });
return; return;
} }
this.goodsList = result.cartList || []; goodsList.value = result.cartList || [];
this.priceDetailDTO = result.priceDetailDTO || {}; priceDetailDTO.value = result.priceDetailDTO || {};
this.skuList = result.skuList || []; skuList.value = result.skuList || [];
if (this.skuList[0]?.goodsSku) { if (skuList.value[0]?.goodsSku) {
this.goodsType = this.skuList[0].goodsSku.goodsType; goodsType.value = skuList.value[0].goodsSku.goodsType;
} }
this.storeId = this.goodsList[0]?.storeId || '' storeId.value = goodsList.value[0]?.storeId || ''
if (result.receiptVO) { if (result.receiptVO) {
this.invoiceData = result.receiptVO; invoiceData.value = result.receiptVO;
} }
let notSupArea = result.notSupportFreight; let notSupArea = result.notSupportFreight;
this.selectedCoupon = {}; selectedCoupon.value = {};
const platformCouponId = result.platformCoupon?.memberCoupon?.id; const platformCouponId = result.platformCoupon?.memberCoupon?.id;
if (platformCouponId) if (platformCouponId)
this.selectedCoupon[platformCouponId] = result.platformCoupon; selectedCoupon.value[platformCouponId] = result.platformCoupon;
if ( if (
result.storeCoupons && result.storeCoupons &&
Object.keys(result.storeCoupons)[0] Object.keys(result.storeCoupons)[0]
@@ -567,7 +578,7 @@ export default {
let storeCouponId = let storeCouponId =
result.storeCoupons[storeMemberCouponsId]?.memberCoupon?.id; result.storeCoupons[storeMemberCouponsId]?.memberCoupon?.id;
if (storeCouponId) if (storeCouponId)
this.selectedCoupon[storeCouponId] = selectedCoupon.value[storeCouponId] =
result.storeCoupons[storeMemberCouponsId]; result.storeCoupons[storeMemberCouponsId];
} }
if (notSupArea) { if (notSupArea) {
@@ -583,39 +594,39 @@ export default {
}); });
} }
if (result.memberAddress) { if (result.memberAddress) {
this.selectedAddress = result.memberAddress; selectedAddress.value = result.memberAddress;
} }
this.getAddress(); getAddress();
this.getStoreAddressList(); getStoreAddressList();
this.totalNum = 0; totalNum.value = 0;
for (let i = 0; i < this.skuList.length; i++) { for (let i = 0; i < skuList.value.length; i++) {
this.totalNum += this.skuList[i].num || 0; totalNum.value += skuList.value[i].num || 0;
} }
this.usedCouponId = []; usedCouponId.value = [];
this.couponList = result.canUseCoupons || []; couponList.value = result.canUseCoupons || [];
const couponKeys = Object.keys(this.selectedCoupon); const couponKeys = Object.keys(selectedCoupon.value);
if (couponKeys.length) { if (couponKeys.length) {
this.couponList.forEach((e) => { couponList.value.forEach((e) => {
if ( if (
this.selectedCoupon[e.id] && selectedCoupon.value[e.id] &&
e.id === this.selectedCoupon[e.id]?.memberCoupon?.id e.id === selectedCoupon.value[e.id]?.memberCoupon?.id
) { ) {
this.usedCouponId.push(e.id); usedCouponId.value.push(e.id);
} }
}); });
this.$nextTick(() => { nextTick(() => {
this.$forceUpdate(); instance.proxy.$forceUpdate();
}); });
} }
this.giftCardDeductItems = Array.isArray(result.giftCardDeductItems) giftCardDeductItems.value = Array.isArray(result.giftCardDeductItems)
? result.giftCardDeductItems ? result.giftCardDeductItems
: []; : [];
this.tradeGiftCardDeductTotal = tradeGiftCardDeductTotal.value =
result.giftCardDeductTotal != null && result.giftCardDeductTotal !== "" result.giftCardDeductTotal != null && result.giftCardDeductTotal !== ""
? Number(result.giftCardDeductTotal) ? Number(result.giftCardDeductTotal)
: null; : null;
this.giftCardList = Array.isArray(result.canUseGiftCards) ? result.canUseGiftCards : []; giftCardList.value = Array.isArray(result.canUseGiftCards) ? result.canUseGiftCards : [];
this.syncGiftCardSelectionFromCart(result); syncGiftCardSelectionFromCart(result);
} }
}) })
.catch((err) => { .catch((err) => {
@@ -626,22 +637,22 @@ export default {
} }
console.warn("获取结算数据失败", err); console.warn("获取结算数据失败", err);
}); });
}, }
syncGiftCardSelectionFromCart(result) { function syncGiftCardSelectionFromCart(result) {
if (!result) { if (!result) {
this.selectedGiftCardCredentialIds = []; selectedGiftCardCredentialIds.value = [];
return; return;
} }
const selectedIds = result.selectedGiftCardIds; const selectedIds = result.selectedGiftCardIds;
if (Array.isArray(selectedIds) && selectedIds.length) { if (Array.isArray(selectedIds) && selectedIds.length) {
this.selectedGiftCardCredentialIds = selectedIds selectedGiftCardCredentialIds.value = selectedIds
.map((id) => (id != null && id !== "" ? String(id) : "")) .map((id) => (id != null && id !== "" ? String(id) : ""))
.filter(Boolean); .filter(Boolean);
return; return;
} }
const items = Array.isArray(result.giftCardDeductItems) ? result.giftCardDeductItems : []; const items = Array.isArray(result.giftCardDeductItems) ? result.giftCardDeductItems : [];
if (items.length) { if (items.length) {
this.selectedGiftCardCredentialIds = items selectedGiftCardCredentialIds.value = items
.map((it) => (it.credentialId != null && it.credentialId !== "" ? String(it.credentialId) : "")) .map((it) => (it.credentialId != null && it.credentialId !== "" ? String(it.credentialId) : ""))
.filter(Boolean); .filter(Boolean);
return; return;
@@ -655,10 +666,10 @@ export default {
(result.selectedGiftCard && (result.selectedGiftCard.id || result.selectedGiftCard.credentialId)) || (result.selectedGiftCard && (result.selectedGiftCard.id || result.selectedGiftCard.credentialId)) ||
(result.giftCardCashSelected && result.giftCardCashSelected.id) || (result.giftCardCashSelected && result.giftCardCashSelected.id) ||
null; null;
this.selectedGiftCardCredentialIds = selectedGiftCardCredentialIds.value =
id != null && id !== "" ? [String(id)] : []; id != null && id !== "" ? [String(id)] : [];
}, }
formatGiftFaceValue(val) { function formatGiftFaceValue(val) {
if (val == null || val === "") { if (val == null || val === "") {
return "—"; return "—";
} }
@@ -667,8 +678,8 @@ export default {
return String(val); return String(val);
} }
return Number.isInteger(n) ? String(n) : n.toFixed(2).replace(/\.?0+$/, ""); return Number.isInteger(n) ? String(n) : n.toFixed(2).replace(/\.?0+$/, "");
}, }
formatGiftExpire(row) { function formatGiftExpire(row) {
const t = row && row.expireTime; const t = row && row.expireTime;
if (!t) { if (!t) {
return "长期有效"; return "长期有效";
@@ -684,27 +695,27 @@ export default {
const m = String(d.getMonth() + 1).padStart(2, "0"); const m = String(d.getMonth() + 1).padStart(2, "0");
const day = String(d.getDate()).padStart(2, "0"); const day = String(d.getDate()).padStart(2, "0");
return `${y}-${m}-${day} 到期`; return `${y}-${m}-${day} 到期`;
}, }
toggleGiftCard(item) { function toggleGiftCard(item) {
if (!item || item.id == null || item.id === "") { if (!item || item.id == null || item.id === "") {
return; return;
} }
if (this.isGiftCardSelected(item)) { if (isGiftCardSelected(item)) {
this.useGiftCard(item.id, false); useGiftCard(item.id, false);
} else { } else {
this.useGiftCard(item.id, true); useGiftCard(item.id, true);
} }
}, }
isGiftCardSelected(item) { function isGiftCardSelected(item) {
if (!item || !this.selectedGiftCardCredentialIds.length) { if (!item || !selectedGiftCardCredentialIds.value.length) {
return false; return false;
} }
const id = item.id != null ? String(item.id) : ""; const id = item.id != null ? String(item.id) : "";
return id && this.selectedGiftCardCredentialIds.includes(id); return id && selectedGiftCardCredentialIds.value.includes(id);
}, }
useGiftCard(credentialId, used) { function useGiftCard(credentialId, used) {
const params = { const params = {
way: this.$route.query.way, way: route.query.way,
used, used,
}; };
if (credentialId != null && credentialId !== "") { if (credentialId != null && credentialId !== "") {
@@ -712,21 +723,21 @@ export default {
} }
selectGiftCard(params).then((res) => { selectGiftCard(params).then((res) => {
if (res.success) { if (res.success) {
this.init(); init();
} }
}).catch((err) => { }).catch((err) => {
// 网络/登录异常已由 request 层统一提示 // 网络/登录异常已由 request 层统一提示
console.warn("选择礼品卡失败", err); console.warn("选择礼品卡失败", err);
}); });
}, }
getCouponNum() { function getCouponNum() {
// 获取可用优惠券数量 // 获取可用优惠券数量
couponNum({ way: this.$route.query.way }).then((res) => { couponNum({ way: route.query.way }).then((res) => {
this.canUseCouponNum = res.result; canUseCouponNum.value = res.result;
if (res.result) { if (res.result) {
let storeArr = []; let storeArr = [];
let skuArr = []; let skuArr = [];
this.goodsList.forEach((e) => { goodsList.value.forEach((e) => {
storeArr.push(e.storeId); storeArr.push(e.storeId);
e.skuList?.forEach((i) => { e.skuList?.forEach((i) => {
skuArr.push(i.goodsSku?.id); skuArr.push(i.goodsSku?.id);
@@ -738,21 +749,21 @@ export default {
memberCouponStatus: "NEW", memberCouponStatus: "NEW",
scopeId: skuArr.toString(), scopeId: skuArr.toString(),
storeId: storeArr.toString(), storeId: storeArr.toString(),
totalPrice: this.priceDetailDTO.goodsPrice, totalPrice: priceDetailDTO.value.goodsPrice,
}; };
canUseCouponList(params).then((res) => { canUseCouponList(params).then((res) => {
// 可用优惠券列表 // 可用优惠券列表
if (res.success) this.couponList = res.result?.records || []; if (res.success) couponList.value = res.result?.records || [];
const couponKeys = Object.keys(this.selectedCoupon); const couponKeys = Object.keys(selectedCoupon.value);
this.usedCouponId = []; usedCouponId.value = [];
if (couponKeys.length) { if (couponKeys.length) {
this.couponList.forEach((e) => { couponList.value.forEach((e) => {
if (e.id === this.selectedCoupon[couponKeys]?.memberCoupon?.id) { if (e.id === selectedCoupon.value[couponKeys]?.memberCoupon?.id) {
this.usedCouponId.push(e.id); usedCouponId.value.push(e.id);
} }
}); });
this.$nextTick(() => { nextTick(() => {
this.$forceUpdate(); instance.proxy.$forceUpdate();
}); });
} }
}).catch((err) => { }).catch((err) => {
@@ -764,87 +775,89 @@ export default {
// 网络/登录异常已由 request 层统一提示 // 网络/登录异常已由 request 层统一提示
console.warn("获取可用优惠券数量失败", err); console.warn("获取可用优惠券数量失败", err);
}); });
}, }
remarkKey(shop) { function remarkKey(shop) {
return shop.storeId || shop.storeName; return shop.storeId || shop.storeName;
}, }
remarkInputRef(shop) { function remarkInputRef(shop) {
return "remarkInput" + this.remarkKey(shop); return (el) => {
}, remarkInputRefs[remarkKey(shop)] = el;
isRemarkOpen(shop) { };
return !!this.remarkEditing[this.remarkKey(shop)]; }
}, function isRemarkOpen(shop) {
openRemark(shop) { return !!remarkEditing.value[remarkKey(shop)];
const key = this.remarkKey(shop); }
this.remarkEditing = { ...this.remarkEditing, [key]: true }; function openRemark(shop) {
this.$nextTick(() => { const key = remarkKey(shop);
const input = this.$refs[this.remarkInputRef(shop)]; remarkEditing.value = { ...remarkEditing.value, [key]: true };
nextTick(() => {
const input = remarkInputRefs[remarkKey(shop)];
const target = Array.isArray(input) ? input[0] : input; const target = Array.isArray(input) ? input[0] : input;
target?.focus?.(); target?.focus?.();
}); });
}, }
closeRemark(shop) { function closeRemark(shop) {
if (shop.remark) { if (shop.remark) {
shop.remark = String(shop.remark).trim(); shop.remark = String(shop.remark).trim();
} }
const key = this.remarkKey(shop); const key = remarkKey(shop);
this.remarkEditing = { ...this.remarkEditing, [key]: false }; remarkEditing.value = { ...remarkEditing.value, [key]: false };
}, }
selectAddress(item) { function selectAddress(item) {
// 选择地址 // 选择地址
let params = { let params = {
way: this.$route.query.way, way: route.query.way,
shippingAddressId: item.id, shippingAddressId: item.id,
}; };
selectAddr(params).then((res) => { selectAddr(params).then((res) => {
if (res.success) { if (res.success) {
Message.success("选择收货地址成功"); Message.success("选择收货地址成功");
this.selectMethod = item; selectMethod.value = item;
this.getGoodsDetail(); getGoodsDetail();
} }
}).catch((err) => { }).catch((err) => {
// 网络/登录异常已由 request 层统一提示 // 网络/登录异常已由 request 层统一提示
console.warn("选择收货地址失败", err); console.warn("选择收货地址失败", err);
}); });
}, }
selectStoreAddress(item) { function selectStoreAddress(item) {
// 选择自提地址 // 选择自提地址
setStoreAddressId(item.id,this.$route.query.way).then((res) => { setStoreAddressId(item.id, route.query.way).then((res) => {
if (res.success) { if (res.success) {
Message.success("选择自提地址成功"); Message.success("选择自提地址成功");
this.selectedStoreAddress = item; selectedStoreAddress.value = item;
this.getGoodsDetail(); getGoodsDetail();
} }
}).catch((err) => { }).catch((err) => {
// 网络/登录异常已由 request 层统一提示 // 网络/登录异常已由 request 层统一提示
console.warn("选择自提地址失败", err); console.warn("选择自提地址失败", err);
}); });
}, }
selectDeliverMethod(item) { function selectDeliverMethod(item) {
let params = { let params = {
way: this.$route.query.way, way: route.query.way,
shippingMethod: item.value, shippingMethod: item.value,
}; };
setShipMethod(params).then((res) => { setShipMethod(params).then((res) => {
if (res.success) { if (res.success) {
this.selectedDeliverMethod = item.value; selectedDeliverMethod.value = item.value;
this.getGoodsDetail(); getGoodsDetail();
} }
}).catch((err) => { }).catch((err) => {
// 网络/登录异常已由 request 层统一提示 // 网络/登录异常已由 request 层统一提示
console.warn("切换配送方式失败", err); console.warn("切换配送方式失败", err);
}); });
}, }
editAddress(id) { function editAddress(id) {
// 编辑地址 // 编辑地址
this.addrId = id; addrId.value = id;
this.$refs.address.show(); address.value.show();
}, }
addrChange() { function addrChange() {
// 添加,编辑地址回显 // 添加,编辑地址回显
this.getAddress(); getAddress();
}, }
delAddress(item) { function delAddress(item) {
// 删除地址 // 删除地址
Modal.confirm({ Modal.confirm({
title: "提示", title: "提示",
@@ -853,7 +866,7 @@ export default {
delMemberAddress(item.id).then((res) => { delMemberAddress(item.id).then((res) => {
if (res.success) { if (res.success) {
Message.success("删除成功"); Message.success("删除成功");
this.getAddress(); getAddress();
} }
}).catch((err) => { }).catch((err) => {
// 网络/登录异常已由 request 层统一提示 // 网络/登录异常已由 request 层统一提示
@@ -862,56 +875,56 @@ export default {
}, },
onCancel: () => { }, onCancel: () => { },
}); });
}, }
goGoodsDetail(skuId, goodsId) { function goGoodsDetail(skuId, goodsId) {
// 跳转商品详情 // 跳转商品详情
let routeUrl = this.$router.resolve({ let routeUrl = router.resolve({
path: "/goodsDetail", path: "/goodsDetail",
query: { skuId, goodsId }, query: { skuId, goodsId },
}); });
window.open(routeUrl.href, "_blank"); window.open(routeUrl.href, "_blank");
}, }
// 跳转店铺首页 // 跳转店铺首页
goShopPage(id) { function goShopPage(id) {
this.$router.push({ router.push({
path: "/merchant", path: "/merchant",
query: { id: id }, query: { id: id },
}); });
}, }
useCoupon(id, used) { function useCoupon(id, used) {
// 使用优惠券 // 使用优惠券
let params = { let params = {
way: this.$route.query.way, way: route.query.way,
memberCouponId: id, memberCouponId: id,
used: used, // true 为使用, false为弃用 used: used, // true 为使用, false为弃用
}; };
selectCoupon(params).then((res) => { selectCoupon(params).then((res) => {
if (res.success) this.init(); if (res.success) init();
}).catch((err) => { }).catch((err) => {
// 网络/登录异常已由 request 层统一提示 // 网络/登录异常已由 request 层统一提示
console.warn("使用优惠券失败", err); console.warn("使用优惠券失败", err);
}); });
}, }
editInvoice() { function editInvoice() {
// 编辑发票信息 // 编辑发票信息
this.$refs.invModal.invoiceAvailable = true; invModal.value.invoiceAvailable = true;
}, }
getInvMsg(item) { function getInvMsg(item) {
// 获取发票信息 // 获取发票信息
if (item) { if (item) {
this.init(); init();
this.$refs.invModal.invoiceAvailable = false; invModal.value.invoiceAvailable = false;
} }
}, }
pay() { function pay() {
// 结算 // 结算
const params = { const params = {
client: "PC", client: "PC",
remark: [], remark: [],
way: this.$route.query.way, way: route.query.way,
}; };
this.goodsList.forEach((e) => { goodsList.value.forEach((e) => {
if (e.remark) { if (e.remark) {
params.remark.push({ params.remark.push({
remark: e.remark, remark: e.remark,
@@ -929,10 +942,10 @@ export default {
if (res.success) { if (res.success) {
const way = params.way; const way = params.way;
if (shouldSkipPaymentPage(res.result, way)) { if (shouldSkipPaymentPage(res.result, way)) {
this.$router.push("/payDone"); router.push("/payDone");
return; return;
} }
this.$router.push({ router.push({
path: "/payment", path: "/payment",
query: { orderType: "TRADE", sn: res.result.sn }, query: { orderType: "TRADE", sn: res.result.sn },
}); });
@@ -948,9 +961,9 @@ export default {
} }
console.warn("提交订单失败", err); console.warn("提交订单失败", err);
}); });
}, }
// 优惠券可用范围 // 优惠券可用范围
useScope(type, storeName) { function useScope(type, storeName) {
let shop = "平台"; let shop = "平台";
let goods = "全部商品"; let goods = "全部商品";
if (storeName !== "platform") shop = storeName; if (storeName !== "platform") shop = storeName;
@@ -966,9 +979,7 @@ export default {
break; break;
} }
return `${shop}${goods}可用`; return `${shop}${goods}可用`;
}, }
},
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -12,13 +12,14 @@
</div> </div>
</template> </template>
<script> <script setup>
export default { import { onMounted } from "vue";
name: 'PayDone',
mounted () { defineOptions({ name: 'PayDone' });
onMounted(() => {
document.querySelector('.pay-done').style.height = window.innerHeight + 'px' document.querySelector('.pay-done').style.height = window.innerHeight + 'px'
} });
};
</script> </script>
<style scoped lang='scss'> <style scoped lang='scss'>

View File

@@ -35,72 +35,71 @@
<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;
if (diff <= 0) return this.endText;
const days = Math.floor(diff / 86400000); const days = Math.floor(diff / 86400000);
const hours = Math.floor((diff % 86400000) / 3600000); const hours = Math.floor((diff % 86400000) / 3600000);
const minutes = Math.floor((diff % 3600000) / 60000); const minutes = Math.floor((diff % 3600000) / 60000);
const seconds = Math.floor((diff % 60000) / 1000); const seconds = Math.floor((diff % 60000) / 1000);
return `${days}天${hours}小时${minutes}分钟${seconds}秒`; return `${days}天${hours}小时${minutes}分钟${seconds}秒`;
}, });
},
methods: { // 获取订单详情
// 获取订单详情 function getTradeDetail() {
getTradeDetail () { const params = route.query;
const params = this.$route.query;
params.clientType = 'PC' params.clientType = 'PC'
tradeDetail(params).then(res => { tradeDetail(params).then(res => {
if (res.success) { if (res.success) {
this.payDetail = res.result; payDetail.value = res.result;
this.endTime = this.payDetail.autoCancel endTime.value = payDetail.value.autoCancel
this.isStart = true isStart.value = true
this.support = this.payDetail.support support.value = payDetail.value.support
this.walletValue = this.payDetail.walletValue walletValue.value = payDetail.value.walletValue
const price = Number(this.payDetail.price); const price = Number(payDetail.value.price);
if (!Number.isNaN(price) && price <= 0) { if (!Number.isNaN(price) && price <= 0) {
this.$router.replace('/payDone'); router.replace('/payDone');
} }
} }
}).catch((err) => { }).catch((err) => {
this.$router.push({name: 'MyOrder'}); router.push({name: 'MyOrder'});
}); });
}, }
// 支付
handlePay (way) { // 支付
function handlePay(way) {
// 余额支付则直接跳转 // 余额支付则直接跳转
if (way === 'WALLET') { if (way === 'WALLET') {
// 如果待支付金额大于余额,则报错 // 如果待支付金额大于余额,则报错
if (this.payDetail.price > this.walletValue) { if (payDetail.value.price > walletValue.value) {
Message.error('余额不足以支付当前订单,如需充值请前往会员中心'); Message.error('余额不足以支付当前订单,如需充值请前往会员中心');
return; return;
} }
} }
const params = this.$route.query; const params = route.query;
params.paymentMethod = way; params.paymentMethod = way;
params.paymentClient = 'NATIVE'; params.paymentClient = 'NATIVE';
params.price = this.payDetail.price; params.price = payDetail.value.price;
if (way === 'WALLET') { if (way === 'WALLET') {
Modal.confirm({ Modal.confirm({
title: '支付确认', title: '支付确认',
@@ -110,7 +109,7 @@ export default {
.then((res) => { .then((res) => {
if (res.success) { if (res.success) {
Message.success(res.message || '支付成功'); Message.success(res.message || '支付成功');
this.$router.push('/payDone'); router.push('/payDone');
} else { } else {
Message.warning(res.message || '支付失败'); Message.warning(res.message || '支付失败');
} }
@@ -124,20 +123,20 @@ export default {
} }
}); });
} else { } else {
this.$router.push({path: '/qrpay', query: params}); router.push({path: '/qrpay', query: params});
} }
} }
},
mounted () { onMounted(() => {
this.getTradeDetail(); getTradeDetail();
this.countdownTimer = setInterval(() => { countdownTimer = setInterval(() => {
this.now = Date.now(); now.value = Date.now();
}, 1000); }, 1000);
}, });
beforeUnmount () {
if (this.countdownTimer) clearInterval(this.countdownTimer); onBeforeUnmount(() => {
}, if (countdownTimer) clearInterval(countdownTimer);
}; });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.head-left { .head-left {

View File

@@ -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) {
},
methods: {
formatPayError (rawMessage) {
const message = (rawMessage || '').trim(); const message = (rawMessage || '').trim();
if (!message || message === '支付业务异常,请稍后重试') { if (!message || message === '支付业务异常,请稍后重试') {
return `${this.payWayName}支付暂不可用,请稍后重试或选择其他支付方式`; return `${payWayName.value}支付暂不可用,请稍后重试或选择其他支付方式`;
} }
return message; return message;
}, }
clearPayPoll () {
if (this.interval) { function clearPayPoll () {
clearInterval(this.interval); if (interval) {
this.interval = null; clearInterval(interval);
interval = null;
} }
}, }
handlePay () {
this.clearPayPoll(); function handlePay () {
const params = this.$route.query; clearPayPoll();
this.payLoading = true; const params = route.query;
this.payError = ''; payLoading.value = true;
this.qrcode = ''; payError.value = '';
qrcode.value = '';
pay(params, { skipMessage: true }) pay(params, { skipMessage: true })
.then((res) => { .then((res) => {
this.payLoading = false; payLoading.value = false;
if (res.success && res.result) { if (res.success && res.result) {
this.qrcode = res.result; qrcode.value = res.result;
this.num = 0; num.value = 0;
this.interval = setInterval(this.callback, 5000); interval = setInterval(callback, 5000);
return; return;
} }
this.payError = this.formatPayError(res.message); payError.value = formatPayError(res.message);
Message.error(this.payError); Message.error(payError.value);
}) })
.catch((err) => { .catch((err) => {
this.payLoading = false; payLoading.value = false;
this.payError = this.formatPayError(err?.message || err?.data?.message); payError.value = formatPayError(err?.message || err?.data?.message);
Message.error(this.payError); Message.error(payError.value);
}); });
}, }
callback () {
this.num++; function callback () {
if (this.num >= 7) { num.value++;
this.clearPayPoll(); if (num.value >= 7) {
clearPayPoll();
} }
const params = JSON.parse(JSON.stringify(this.$route.query)); const params = JSON.parse(JSON.stringify(route.query));
delete params.paymentMethod; delete params.paymentMethod;
delete params.paymentClient; delete params.paymentClient;
payCallback(params).then((res) => { payCallback(params).then((res) => {
if (res.result) { if (res.result) {
this.clearPayPoll(); clearPayPoll();
this.$router.push({ path: '/payDone', query: { orderType: this.$route.query.orderType } }); router.push({ path: '/payDone', query: { orderType: route.query.orderType } });
} }
}); });
}, }
},
mounted () { onMounted(() => {
this.handlePay(); handlePay();
}, });
beforeUnmount () {
this.clearPayPoll(); onBeforeUnmount(() => {
}, clearPayPoll();
}; });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.head-left { .head-left {

View File

@@ -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,
count: 1,
loading: false,
};
},
computed: {
isECouponSku() {
return isECoupon(this.goodsSku.goodsType);
},
displayStock() {
if (this.isECouponSku) {
return getECouponStock(this.goodsSku);
} }
return Number(this.goodsSku.quantity) || 0; return Number(goodsSku.value.quantity) || 0;
}, });
quantityMax() { const quantityMax = computed(() => {
if (this.isECouponSku) { if (isECouponSku.value) {
return getECouponMaxBuyNum(this.goodsSku); return getECouponMaxBuyNum(goodsSku.value);
} }
const qty = Number(this.goodsSku.quantity) || 0; const qty = Number(goodsSku.value.quantity) || 0;
return qty > 0 ? qty : 1; return qty > 0 ? qty : 1;
}, });
isOutOfStock() { const isOutOfStock = computed(() => {
return this.displayStock <= 0; return displayStock.value <= 0;
}, });
},
methods: { function getGoodsDetail() {
getGoodsDetail() { isLoading.value = true;
this.isLoading = true; pointGoodsDetail(route.query.id)
pointGoodsDetail(this.$route.query.id)
.then((res) => { .then((res) => {
this.isLoading = false; isLoading.value = false;
if (res.success) { if (res.success) {
this.goodsMsg = res.result; goodsMsg.value = res.result;
this.goodsSku = res.result.goodsSku || {}; goodsSku.value = res.result.goodsSku || {};
this.syncCountWithQuantity(); syncCountWithQuantity();
} else { } else {
Message.error(res.message); Message.error(res.message);
this.$router.push("/"); router.push("/");
} }
}) })
.catch(() => { .catch(() => {
this.$router.push("/"); router.push("/");
}); });
}, }
syncCountWithQuantity() {
const max = this.quantityMax; function syncCountWithQuantity() {
const max = quantityMax.value;
if (max <= 0) { if (max <= 0) {
this.count = 1; count.value = 1;
return; return;
} }
if (this.count > max) this.count = max; if (count.value > max) count.value = max;
if (this.count < 1) this.count = 1; if (count.value < 1) count.value = 1;
}, }
pointBuy() {
function pointBuy() {
const params = { const params = {
num: this.count, num: count.value,
skuId: this.goodsMsg.skuId, skuId: goodsMsg.value.skuId,
cartType: E_COUPON_CART_POINTS, cartType: E_COUPON_CART_POINTS,
}; };
this.loading = true; loading.value = true;
addCartGoods(params) addCartGoods(params)
.then((res) => { .then((res) => {
this.loading = false; loading.value = false;
if (res.success) { if (res.success) {
this.$router.push({ path: "/pay", query: { way: params.cartType } }); router.push({ path: "/pay", query: { way: params.cartType } });
} else { } else {
Message.warning(res.message); Message.warning(res.message);
} }
}) })
.catch(() => { .catch(() => {
this.loading = false; loading.value = false;
}); });
}, }
handleScroll() {
if (this.onceFlag) { function handleScroll() {
this.$nextTick(() => { if (onceFlag.value) {
this.changeHeight(); nextTick(() => {
changeHeight();
}); });
this.onceFlag = false; onceFlag.value = false;
} }
}, }
changeHeight() {
function changeHeight() {
const goodsDetailCon = document.querySelector(".item-intro"); const goodsDetailCon = document.querySelector(".item-intro");
if (!goodsDetailCon || !this.$refs.itemIntroGoods) return; if (!goodsDetailCon || !itemIntroGoods.value) return;
let heightCss = window.getComputedStyle(goodsDetailCon).height; let heightCss = window.getComputedStyle(goodsDetailCon).height;
heightCss = parseInt(heightCss.substr(0, heightCss.length - 2), 10) + 89; heightCss = parseInt(heightCss.substr(0, heightCss.length - 2), 10) + 89;
this.$refs.itemIntroGoods.style.height = heightCss + "px"; itemIntroGoods.value.style.height = heightCss + "px";
}, }
},
}; getGoodsDetail();
onMounted(() => {
window.addEventListener("scroll", handleScroll);
});
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -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: "全部分类",
id: 0,
}]); // 积分分类列表
// 商品列表请求参数
const params = reactive({
pageNumber: 1, pageNumber: 1,
pageSize: 20, pageSize: 20,
pointsGoodsCategoryId: '' pointsGoodsCategoryId: "",
}, });
total: 0, // 商品总数 const total = ref(0); // 商品总数
cateId: '' // 店铺分类id const cateId = ref(""); // 店铺分类id
}
}, onMounted(() => {
mounted () { params.pointsGoodsCategoryId = route.query.categoryId || "";
this.params.pointsGoodsCategoryId = this.$route.query.categoryId || '' cateId.value = route.query.categoryId || 0;
this.cateId = this.$route.query.categoryId || 0 getList();
this.getList() getCate();
this.getCate() });
},
methods: { /** 列表 API 返回 PointsGoods(thumbnail/goodsName 在顶层);详情 API 才有 goodsSku */
/** 列表 API 返回 PointsGoods(thumbnail/goodsName 在顶层);详情 API 才有 goodsSku */ function itemThumbnail(item) {
itemThumbnail (item) { return item?.goodsSku?.thumbnail || item?.thumbnail || "";
return item?.goodsSku?.thumbnail || item?.thumbnail || '' }
},
itemGoodsName (item) { function itemGoodsName(item) {
return item?.goodsSku?.goodsName || item?.goodsName || '' return item?.goodsSku?.goodsName || item?.goodsName || "";
}, }
getList () { // 获取列表
pointGoods(this.params).then(res => { // 获取列表
function getList() {
pointGoods(params).then((res) => {
if (res.success) { if (res.success) {
this.goodsList = res.result.records goodsList.value = res.result.records;
this.total = res.result.total total.value = res.result.total;
} }
}) });
}, }
getCate () { // 获取分类
pointGoodsCategory(this.params).then(res => { // 获取分类
function getCate() {
pointGoodsCategory(params).then((res) => {
if (res.success) { if (res.success) {
this.cateList.push(...res.result.records) cateList.value.push(...res.result.records);
} }
}) });
}, }
selectCate (item) { // 选择商品分类
let cateId = item.value === 0 ? '' : item.value // 选择商品分类
this.params.pointsGoodsCategoryId = cateId function selectCate(item) {
this.getList() let cateId = item.value === 0 ? "" : item.value;
this.$router.push({query: {categoryId: cateId}}) params.pointsGoodsCategoryId = cateId;
}, getList();
goGoodsDetail (id) { // 跳转商品详情 router.push({ query: { categoryId: cateId } });
this.$router.push({path: '/pointGoodsDetail', query: {id}}) }
// let routerUrl = this.$router.resolve({
// 跳转商品详情
function goGoodsDetail(id) {
router.push({ path: "/pointGoodsDetail", query: { id } });
// let routerUrl = router.resolve({
// path: '/pointGoodsDetail', // path: '/pointGoodsDetail',
// query: {id} // query: {id}
// }) // })
// window.open(routerUrl.href, '_blank') // window.open(routerUrl.href, '_blank')
}, }
changePageNum (val) { // 修改页码
this.params.pageNumber = val; // 修改页码
this.getList() function changePageNum(val) {
}, params.pageNumber = val;
changePageSize (val) { // 修改页数 getList();
this.params.pageNumber = 1; }
this.params.pageSize = val;
this.getList() // 修改页数
} function changePageSize(val) {
} params.pageNumber = 1;
params.pageSize = val;
getList();
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@@ -61,32 +61,30 @@
<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);
this.goodsList = this.list[val].seckillGoodsList;
},
diffSeconds (val) {
const hours = Math.floor(val / 3600); const hours = Math.floor(val / 3600);
// 当前秒数 / 60,向下取整 // 当前秒数 / 60,向下取整
// 获取到所有分钟数 3600 / 60 = 60分钟 // 获取到所有分钟数 3600 / 60 = 60分钟
@@ -94,11 +92,11 @@ export default {
const minutes = Math.floor(val / 60) % 60; const minutes = Math.floor(val / 60) % 60;
// 当前的秒数 % 60,获取到 超过小时数、分钟数的秒数(秒数) // 当前的秒数 % 60,获取到 超过小时数、分钟数的秒数(秒数)
const seconds = val % 60; const seconds = val % 60;
this.currTime = currTime.value =
filteTime(hours) + ":" + filteTime(minutes) + ":" + filteTime(seconds); filteTime(hours) + ":" + filteTime(minutes) + ":" + filteTime(seconds);
if (val <= 0) { if (val <= 0) {
clearInterval(this.interval); clearInterval(interval);
this.interval = null; interval = null;
} }
function filteTime (time) { function filteTime (time) {
if (time < 10) { if (time < 10) {
@@ -107,75 +105,83 @@ export default {
return time; return time;
} }
} }
}, });
},
methods: { function getListByDay () {
getListByDay () {
// 当天秒杀活动 // 当天秒杀活动
seckillByDay().then((res) => { seckillByDay().then((res) => {
if (res.success) { if (res.success) {
this.list = res.result; list.value = res.result;
this.goodsList = this.list[0].seckillGoodsList; goodsList.value = list.value[0].seckillGoodsList;
this.countDown(this.currIndex); countDown(currIndex.value);
} }
}); });
}, }
goGoodsDetail (skuId, goodsId) {
function goGoodsDetail (skuId, goodsId) {
// 跳转商品详情 // 跳转商品详情
let routeUrl = this.$router.resolve({ let routeUrl = router.resolve({
path: "/goodsDetail", path: "/goodsDetail",
query: { skuId, goodsId }, query: { skuId, goodsId },
}); });
window.open(routeUrl.href, "_blank"); window.open(routeUrl.href, "_blank");
}, }
// 已售占比。没有库存时按售罄展示
salePercent (item) { // 已售占比。没有库存时按售罄展示
function salePercent (item) {
if (!item.quantity || item.quantity <= 0) return 100; if (!item.quantity || item.quantity <= 0) return 100;
return Math.min(100, Math.ceil((item.salesNum / item.quantity) * 100)); return Math.min(100, Math.ceil((item.salesNum / item.quantity) * 100));
}, }
// 场次已开始,且销量达到库存
isSoldOut (item) { // 场次已开始,且销量达到库存
return this.nowHour >= this.list[this.currIndex].timeLine && item.quantity <= item.salesNum; function isSoldOut (item) {
}, return nowHour.value >= list.value[currIndex.value].timeLine && item.quantity <= item.salesNum;
// 场次已开始且未售罄才可抢 }
canBuy (item) {
return this.nowHour >= this.list[this.currIndex].timeLine && !this.isSoldOut(item); // 场次已开始且未售罄才可抢
}, function canBuy (item) {
// 按钮文案:未开始 / 已售罄 / 立即抢购 return nowHour.value >= list.value[currIndex.value].timeLine && !isSoldOut(item);
buyText (item) { }
if (this.nowHour < this.list[this.currIndex].timeLine) return "即将开始";
if (this.isSoldOut(item)) return "已售罄"; // 按钮文案:未开始 / 已售罄 / 立即抢购
function buyText (item) {
if (nowHour.value < list.value[currIndex.value].timeLine) return "即将开始";
if (isSoldOut(item)) return "已售罄";
return "立即抢购"; return "立即抢购";
}, }
countDown (currIndex) {
function countDown (currIndex) {
// 倒计时 // 倒计时
// 0点时间戳 // 0点时间戳
let zeroTime = new Date(new Date().toLocaleDateString()).getTime(); let zeroTime = new Date(new Date().toLocaleDateString()).getTime();
let currTime = new Date().getTime(); let currTime = new Date().getTime();
let actTime = 0; let actTime = 0;
let nowHour = new Date().getHours(); // 当前小时数 let nowHour = new Date().getHours(); // 当前小时数
if (this.list[currIndex].timeLine > nowHour) { if (list.value[currIndex].timeLine > nowHour) {
// 活动未开始 // 活动未开始
actTime = zeroTime + this.list[currIndex].timeLine * 3600 * 1000; actTime = zeroTime + list.value[currIndex].timeLine * 3600 * 1000;
} else if (this.list[currIndex].timeLine <= nowHour) { } else if (list.value[currIndex].timeLine <= nowHour) {
// 活动进行中 // 活动进行中
if (currIndex === this.list.length - 1) { if (currIndex === list.value.length - 1) {
// 如果是最后一个活动,直到24点结束 // 如果是最后一个活动,直到24点结束
actTime = zeroTime + 24 * 3600 * 1000; actTime = zeroTime + 24 * 3600 * 1000;
} else { } else {
actTime = zeroTime + this.list[currIndex + 1].timeLine * 3600 * 1000; actTime = zeroTime + list.value[currIndex + 1].timeLine * 3600 * 1000;
} }
} }
this.diffSeconds = Math.floor((actTime - currTime) / 1000); diffSeconds.value = Math.floor((actTime - currTime) / 1000);
this.interval = setInterval(() => { interval = setInterval(() => {
this.diffSeconds--; diffSeconds.value--;
}, 1000); }, 1000);
}, }
},
mounted () { onMounted(() => {
this.getListByDay(); getListByDay();
}, });
};
onBeforeUnmount(() => {
clearInterval(interval);
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "../../assets/styles/goodsList.scss"; @import "../../assets/styles/goodsList.scss";

View File

@@ -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,24 +201,24 @@ 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'],
props: {
content: { content: {
default: () => ({}), default: () => ({}),
type: Object type: Object
} }
}, });
data () {
return {
action: buyerUrl + '/buyer/common/upload/file', // 上传地址
accessToken: {}, // 验证token
visible: false, // 预览图片
loading: false, // 加载状态
previewPicture: '', // 预览图片url const firstFormRef = ref(null);
form: { // 表单数据 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: '', companyName: '',
companyAddressIdPath: '', companyAddressIdPath: '',
companyAddressPath: '', companyAddressPath: '',
@@ -234,8 +235,8 @@ export default {
legalId: '', legalId: '',
legalPhoto: [], legalPhoto: [],
licencePhoto: [] licencePhoto: []
}, });
rules: { // 验证规则 const rules = { // 验证规则
companyName: [{ required: true, message: '请填写公司信息' }], companyName: [{ required: true, message: '请填写公司信息' }],
companyAddressIdPath: [{ required: true, message: '请选择公司所在地' }], companyAddressIdPath: [{ required: true, message: '请选择公司所在地' }],
companyAddress: [{ required: true, message: '请填写公司详细地址' }], companyAddress: [{ required: true, message: '请填写公司详细地址' }],
@@ -272,189 +273,187 @@ export default {
{ required: true, message: '请输入法人证件号' }, { required: true, message: '请输入法人证件号' },
{ pattern: RegExp.IDCard, message: '请输入正确的证件号' } { pattern: RegExp.IDCard, message: '请输入正确的证件号' }
] ]
}, };
uploadLoading1: false, // 上传loading const uploadLoading1 = ref(false); // 上传loading
uploadLoading: false, // 上传loading const uploadLoading = ref(false); // 上传loading
contentInitialized: false const contentInitialized = ref(false);
};
}, // 获取店铺地址
methods: { function getAddress(val){
// 获取店铺地址
getAddress(val){
if(val.type === 'select'){ if(val.type === 'select'){
const paths = val.data.map(item => item.name).join(',') const paths = val.data.map(item => item.name).join(',')
const ids = val.data.map(item => item.id).join(',') const ids = val.data.map(item => item.id).join(',')
this.form['companyAddressIdPath'] = ids form.value['companyAddressIdPath'] = ids
this.form['companyAddressPath'] = paths form.value['companyAddressPath'] = paths
}else{ }else{
this.form['companyAddressIdPath'] = val.data.addrId form.value['companyAddressIdPath'] = val.data.addrId
this.form['companyAddressPath'] = val.data.addr form.value['companyAddressPath'] = val.data.addr
} }
}, }
// 下一步 // 下一步
next () { function next () {
this.$refs.firstForm.validate((valid) => { firstFormRef.value.validate((valid) => {
if (valid) { if (valid) {
this.loading = true; loading.value = true;
let params = JSON.parse(JSON.stringify(this.form)); let params = JSON.parse(JSON.stringify(form.value));
params.legalPhoto = this.form.legalPhoto.toString(); params.legalPhoto = form.value.legalPhoto.toString();
params.licencePhoto = this.normalizeLicencePhoto(this.form.licencePhoto); params.licencePhoto = normalizeLicencePhoto(form.value.licencePhoto);
applyFirst(params) applyFirst(params)
.then((res) => { .then((res) => {
this.loading = false; loading.value = false;
if (res.success) { if (res.success) {
sessionStorage.removeItem(FIRST_APPLY_DRAFT_KEY); sessionStorage.removeItem(FIRST_APPLY_DRAFT_KEY);
this.$emit('change', 1); emit('change', 1);
} }
}) })
.catch(() => { .catch(() => {
this.loading = false; loading.value = false;
}); });
} }
}); });
}, }
// 上传之前 // 上传之前
beforeUpload (file) { function beforeUpload (file) {
this.uploadLoading = true; uploadLoading.value = true;
if (this.form.licencePhoto.length >= 1) { if (form.value.licencePhoto.length >= 1) {
Message.warning('最多上传一张图片') Message.warning('最多上传一张图片')
this.uploadLoading = false; uploadLoading.value = false;
return false; return false;
} }
return this.validateUploadFile(file); return validateUploadFile(file);
}, }
// 上传之前 // 上传之前
beforeUpload1 (file) { function beforeUpload1 (file) {
this.uploadLoading1 = true; uploadLoading1.value = true;
if (this.form.legalPhoto.length >= 2) { if (form.value.legalPhoto.length >= 2) {
Message.warning('最多上传两张图片') Message.warning('最多上传两张图片')
this.uploadLoading1 = false; uploadLoading1.value = false;
return false; return false;
} }
return this.validateUploadFile(file); return validateUploadFile(file);
}, }
validateUploadFile (file) { function validateUploadFile (file) {
const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif']; const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'];
const isAllowedType = allowedTypes.includes(file.type); const isAllowedType = allowedTypes.includes(file.type);
const isLt2M = file.size / 1024 / 1024 < 2; const isLt2M = file.size / 1024 / 1024 < 2;
if (!isAllowedType) { if (!isAllowedType) {
this.uploadLoading = false; uploadLoading.value = false;
this.uploadLoading1 = false; uploadLoading1.value = false;
Message.warning('上传文件格式不正确'); Message.warning('上传文件格式不正确');
return false; return false;
} }
if (!isLt2M) { if (!isLt2M) {
this.uploadLoading = false; uploadLoading.value = false;
this.uploadLoading1 = false; uploadLoading1.value = false;
Message.warning('文件大小不能超过2M'); Message.warning('文件大小不能超过2M');
return false; return false;
} }
return true; return true;
}, }
// 上传成功回调 // 上传成功回调
handleSuccess (res, file) { function handleSuccess (res, file) {
this.uploadLoading = false; uploadLoading.value = false;
this.form.licencePhoto = [res.result]; form.value.licencePhoto = [res.result];
}, }
// 上传成功回调 // 上传成功回调
handleSuccess1 (res, file) { function handleSuccess1 (res, file) {
this.uploadLoading1 = false; uploadLoading1.value = false;
this.form.legalPhoto.push(res.result); form.value.legalPhoto.push(res.result);
}, }
// 上传失败 // 上传失败
uploadErr () { function uploadErr () {
this.uploadLoading = false; uploadLoading.value = false;
this.uploadLoading1 = false; uploadLoading1.value = false;
}, }
// 上传失败回调 // 上传失败回调
handleFormatError (file) { function handleFormatError (file) {
this.uploadLoading = false; uploadLoading.value = false;
this.uploadLoading1 = false; uploadLoading1.value = false;
Notice.warning({ Notice.warning({
title: 'The file format is incorrect', title: 'The file format is incorrect',
desc: '上传文件格式不正确' desc: '上传文件格式不正确'
}); });
}, }
// 上传大小限制 // 上传大小限制
handleMaxSize (file) { function handleMaxSize (file) {
this.uploadLoading = false; uploadLoading.value = false;
this.uploadLoading1 = false; uploadLoading1.value = false;
Notice.warning({ Notice.warning({
title: 'Exceeding file size limit', title: 'Exceeding file size limit',
desc: '文件大小不能超过2M' desc: '文件大小不能超过2M'
}); });
}, }
// 图片查看 // 图片查看
handleView (item) { function handleView (item) {
this.previewPicture = item; previewPicture.value = item;
this.visible = true; visible.value = true;
}, }
// 删除图片 // 删除图片
handleRemove (index, listName) { function handleRemove (index, listName) {
this.form[listName].splice(index, 1); form.value[listName].splice(index, 1);
}, }
normalizeLicencePhoto (value) { function normalizeLicencePhoto (value) {
if (Array.isArray(value)) { if (Array.isArray(value)) {
return value.length ? value[0] : ''; return value.length ? value[0] : '';
} }
return value ? String(value).split(',').filter(Boolean)[0] || '' : ''; return value ? String(value).split(',').filter(Boolean)[0] || '' : '';
}, }
initFormFromContent(content) { function initFormFromContent(content) {
if (!content || !Object.keys(content).length) return; if (!content || !Object.keys(content).length) return;
this.form = JSON.parse(JSON.stringify(content)); form.value = JSON.parse(JSON.stringify(content));
this.form.legalPhoto = content.legalPhoto form.value.legalPhoto = content.legalPhoto
? String(content.legalPhoto).split(',').filter(Boolean) ? String(content.legalPhoto).split(',').filter(Boolean)
: []; : [];
this.form.licencePhoto = content.licencePhoto form.value.licencePhoto = content.licencePhoto
? String(content.licencePhoto).split(',').filter(Boolean).slice(0, 1) ? String(content.licencePhoto).split(',').filter(Boolean).slice(0, 1)
: []; : [];
this.contentInitialized = true; contentInitialized.value = true;
}, }
restoreDraft() { function restoreDraft() {
const draft = sessionStorage.getItem(FIRST_APPLY_DRAFT_KEY); const draft = sessionStorage.getItem(FIRST_APPLY_DRAFT_KEY);
if (!draft) return; if (!draft) return;
try { try {
const parsed = JSON.parse(draft); const parsed = JSON.parse(draft);
this.form = { form.value = {
legalPhoto: [], legalPhoto: [],
licencePhoto: [], licencePhoto: [],
...parsed, ...parsed,
}; };
this.form.legalPhoto = Array.isArray(parsed.legalPhoto) ? parsed.legalPhoto : []; form.value.legalPhoto = Array.isArray(parsed.legalPhoto) ? parsed.legalPhoto : [];
this.form.licencePhoto = Array.isArray(parsed.licencePhoto) form.value.licencePhoto = Array.isArray(parsed.licencePhoto)
? parsed.licencePhoto.slice(0, 1) ? parsed.licencePhoto.slice(0, 1)
: []; : [];
} catch (e) { } catch (e) {
sessionStorage.removeItem(FIRST_APPLY_DRAFT_KEY); 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);
}, },
saveDraft() { { immediate: true }
sessionStorage.setItem(FIRST_APPLY_DRAFT_KEY, JSON.stringify(this.form)); );
}
}, onMounted(() => {
watch: { accessToken.accessToken = storage.getItem('accessToken');
content: { if (Object.keys(props.content).length) {
immediate: true, initFormFromContent(props.content);
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 { } else {
this.restoreDraft(); restoreDraft();
} }
}, });
beforeUnmount() {
this.saveDraft(); onBeforeUnmount(() => {
} saveDraft();
}; });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
h4 { h4 {

View File

@@ -40,21 +40,22 @@
</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 { const secondForm = ref(null);
loading: false, // 加载状态 const loading = ref(false); // 加载状态
form: {}, // 表单数据 const form = reactive({}); // 表单数据
rules: { // 验证规则 const rules = { // 验证规则
settlementBankAccountName: [ settlementBankAccountName: [
{ required: true, message: '请填写银行开户名称' } { required: true, message: '请填写银行开户名称' }
], ],
@@ -67,33 +68,30 @@ export default {
settlementBankJointName: [ settlementBankJointName: [
{ required: true, message: '请填写支行联行号' } { required: true, message: '请填写支行联行号' }
] ]
} };
};
}, // 下一步
methods: { function next() {
// 下一步 secondForm.value.validate((valid) => {
next () {
this.$refs.secondForm.validate((valid) => {
if (valid) { if (valid) {
this.loading = true; loading.value = true;
applySecond(this.form) applySecond(form)
.then((res) => { .then((res) => {
this.loading = false; loading.value = false;
if (res.success) this.$emit('change', 2); if (res.success) emit('change', 2);
}) })
.catch(() => { .catch(() => {
this.loading = false; loading.value = false;
}); });
} }
}); });
}
onMounted(() => {
if (props.content && Object.keys(props.content).length) {
Object.assign(form, JSON.parse(JSON.stringify(props.content)));
} }
}, });
mounted () {
if (this.content && Object.keys(this.content).length) {
this.form = JSON.parse(JSON.stringify(this.content));
}
}
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
h4 { h4 {

View File

@@ -56,39 +56,31 @@
</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: { const currentIndex = ref(0); // 当前步骤
firstApply, const showAgreement = ref(false); // 协议显示
secondApply, const agreementCon = ref(""); // 协议内容
thirdApply, const checked = ref(false); // 选中协议
}, const firstData = reactive({}); // 第一步数据
data() { const secondData = reactive({}); // 第二步数据
return { const thirdData = reactive({}); // 第三步数据
currentIndex: 0, // 当前步骤 const storeDisable = ref(""); // APPLY OPEN 开店中 CLOSED 关闭 REFUSED 拒绝 APPLYING 申请中,审核
showAgreement: false, // 协议显示 const initLoading = ref(true);
agreementCon: "", // 协议内容 const agreementAccepted = ref(false); // 是否已同意入驻协议
checked: false, // 选中协议
firstData: {}, // 第一步数据 function confirmAgreement() {
secondData: {}, // 第二步数据 agreementAccepted.value = true;
thirdData: {}, // 第三步数据 showAgreement.value = false;
storeDisable: "", // APPLY OPEN 开店中 CLOSED 关闭 REFUSED 拒绝 APPLYING 申请中,审核
initLoading: true,
agreementAccepted: false, // 是否已同意入驻协议
};
},
methods: {
confirmAgreement() {
this.agreementAccepted = true;
this.showAgreement = false;
sessionStorage.setItem("shopEntryAgreementAccepted", "1"); sessionStorage.setItem("shopEntryAgreementAccepted", "1");
}, }
assignApplyData(data) {
function assignApplyData(data) {
const first = [ const first = [
"companyAddressPath", "companyAddressPath",
"companyAddress", "companyAddress",
@@ -124,71 +116,74 @@ export default {
"storeAddressDetail", "storeAddressDetail",
]; ];
this.storeDisable = data.storeDisable || ""; storeDisable.value = data.storeDisable || "";
Object.assign(this.firstData, Object.fromEntries(first.map((key) => [key, data[key]]))); Object.assign(firstData, Object.fromEntries(first.map((key) => [key, data[key]])));
Object.assign(this.secondData, Object.fromEntries(second.map((key) => [key, data[key]]))); Object.assign(secondData, Object.fromEntries(second.map((key) => [key, data[key]])));
Object.assign(this.thirdData, Object.fromEntries(third.map((key) => [key, data[key]]))); Object.assign(thirdData, Object.fromEntries(third.map((key) => [key, data[key]])));
}, }
getArticle() {
function getArticle() {
// 入驻协议:库中无 STORE_REGISTER 文章时 result 为 null,不能直接读 content // 入驻协议:库中无 STORE_REGISTER 文章时 result 为 null,不能直接读 content
agreement().then((res) => { agreement().then((res) => {
this.agreementCon = res.result?.content || ""; agreementCon.value = res.result?.content || "";
}); });
}, }
getData(status) {
function getData(status) {
applyStatus().then((res) => { applyStatus().then((res) => {
if (!res.success) { if (!res.success) {
return; return;
} }
if (!res.result) { if (!res.result) {
if (status === "init" && !this.agreementAccepted) { if (status === "init" && !agreementAccepted.value) {
this.showAgreement = true; showAgreement.value = true;
} }
return; return;
} }
this.assignApplyData(res.result); assignApplyData(res.result);
if (status === "init") { if (status === "init") {
this.agreementAccepted = true; agreementAccepted.value = true;
sessionStorage.setItem("shopEntryAgreementAccepted", "1"); sessionStorage.setItem("shopEntryAgreementAccepted", "1");
if (this.storeDisable === "APPLY") { if (storeDisable.value === "APPLY") {
this.currentIndex = 0; currentIndex.value = 0;
} else { } else {
this.currentIndex = 3; currentIndex.value = 3;
} }
} }
}).finally(() => { }).finally(() => {
if (status === "init") { if (status === "init") {
this.initLoading = false; initLoading.value = false;
} }
}); });
}, }
refreshApplyData() {
function refreshApplyData() {
return applyStatus().then((res) => { return applyStatus().then((res) => {
if (res.success && res.result) { if (res.success && res.result) {
this.assignApplyData(res.result); assignApplyData(res.result);
} }
}).catch((err) => { }).catch((err) => {
console.warn('获取入驻申请状态失败', err); console.warn('获取入驻申请状态失败', err);
}); });
}, }
// 下一步:先拉最新申请数据,再切步骤,避免第三步挂载后再改 content 触发校验
async nextPage(step) { // 下一步:先拉最新申请数据,再切步骤,避免第三步挂载后再改 content 触发校验
async function nextPage(step) {
if (typeof step !== "number") return; if (typeof step !== "number") return;
if (step > 0 && step <= 3) { if (step > 0 && step <= 3) {
await this.refreshApplyData(); await refreshApplyData();
} }
this.currentIndex = step; currentIndex.value = step;
}, }
},
created() { // 初始化(原 created 钩子逻辑)
this.agreementAccepted = sessionStorage.getItem("shopEntryAgreementAccepted") === "1"; agreementAccepted.value = sessionStorage.getItem("shopEntryAgreementAccepted") === "1";
},
mounted() { onMounted(() => {
this.getData("init"); getData("init");
this.getArticle(); getArticle();
}, });
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.content { .content {

View File

@@ -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,25 +106,25 @@ 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: { const props = defineProps({
content: { content: {
default: () => ({}), default: () => ({}),
type: Object type: Object
} }
}, });
components: { multipleMap, View, Delete },
data () {
return {
loading: false, // 加载状态
uploadLoading: false, // 上传加载状态
action: buyerUrl + '/buyer/common/upload/file', // 上传地址
accessToken: {}, // 验证token
previewPicture: '', // 预览图片
visible: false, // 图片预览 const thirdFormRef = ref(null);
form: { // 表单数据
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: [], storeLogo: [],
goodsManagementCategory: [], goodsManagementCategory: [],
storeName: "", storeName: "",
@@ -132,8 +133,8 @@ export default {
storeAddressIdPath: "", storeAddressIdPath: "",
storeAddressPath: "", storeAddressPath: "",
storeCenter: "", storeCenter: "",
}, });
rules: { // 验证规则 const rules = { // 验证规则
goodsManagementCategory: [ goodsManagementCategory: [
{ required: true, message: '请选择店铺经营类目' } { required: true, message: '请选择店铺经营类目' }
], ],
@@ -143,112 +144,110 @@ export default {
storeCenter: [{ required: true, message: '请选择店铺位置' }], storeCenter: [{ required: true, message: '请选择店铺位置' }],
storeAddressIdPath: [{ required: true, message: '请选择店铺位置' }], storeAddressIdPath: [{ required: true, message: '请选择店铺位置' }],
storeAddressDetail: [{ required: true, message: '请输入店铺详细地址' }] storeAddressDetail: [{ required: true, message: '请输入店铺详细地址' }]
}, };
categoryList: [] // 分类数据 const categoryList = ref([]); // 分类数据
};
}, // 下一步
methods: { function next () {
// 下一步 thirdFormRef.value.validate((valid) => {
next () {
this.$refs.thirdForm.validate((valid) => {
if (valid) { if (valid) {
this.loading = true; loading.value = true;
let params = JSON.parse(JSON.stringify(this.form)); let params = JSON.parse(JSON.stringify(form.value));
params.storeLogo = this.form.storeLogo.toString(); params.storeLogo = form.value.storeLogo.toString();
params.goodsManagementCategory = this.form.goodsManagementCategory.toString(); params.goodsManagementCategory = form.value.goodsManagementCategory.toString();
applyThird(params) applyThird(params)
.then((res) => { .then((res) => {
this.loading = false; loading.value = false;
if (res.success) this.$emit('change', 3); if (res.success) emit('change', 3);
}) })
.catch(() => { .catch(() => {
this.loading = false; loading.value = false;
}); });
} }
}); });
}, }
// 上传之前 // 上传之前
beforeUpload (file) { function beforeUpload (file) {
this.uploadLoading = true; uploadLoading.value = true;
if (this.form.storeLogo.length >= 1) { if (form.value.storeLogo.length >= 1) {
Message.warning('最多上传一张图片') Message.warning('最多上传一张图片')
this.uploadLoading = false; uploadLoading.value = false;
return false; return false;
} }
return this.validateUploadFile(file); return validateUploadFile(file);
}, }
validateUploadFile (file) { function validateUploadFile (file) {
const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif']; const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'];
const isAllowedType = allowedTypes.includes(file.type); const isAllowedType = allowedTypes.includes(file.type);
const isLt2M = file.size / 1024 / 1024 < 2; const isLt2M = file.size / 1024 / 1024 < 2;
if (!isAllowedType) { if (!isAllowedType) {
this.uploadLoading = false; uploadLoading.value = false;
Message.warning('上传文件格式不正确'); Message.warning('上传文件格式不正确');
return false; return false;
} }
if (!isLt2M) { if (!isLt2M) {
this.uploadLoading = false; uploadLoading.value = false;
Message.warning('文件大小不能超过2M'); Message.warning('文件大小不能超过2M');
return false; return false;
} }
return true; return true;
}, }
// 上传成功回调 // 上传成功回调
handleSuccess (res, file) { function handleSuccess (res, file) {
this.uploadLoading = false; uploadLoading.value = false;
this.form.storeLogo = [res.result]; form.value.storeLogo = [res.result];
}, }
// 上传失败 // 上传失败
uploadErr () { function uploadErr () {
this.uploadLoading = false; uploadLoading.value = false;
}, }
// 查看图片 // 查看图片
handleView (item) { function handleView (item) {
this.previewPicture = item; previewPicture.value = item;
this.visible = true; visible.value = true;
}, }
// 删除图片 // 删除图片
handleRemove (index, listName) { function handleRemove (index, listName) {
this.form[listName].splice(index, 1); form.value[listName].splice(index, 1);
}, }
// 选择坐标回调 // 选择坐标回调
getAddress (val) { function getAddress (val) {
if(val.type === 'select'){ if(val.type === 'select'){
const paths = val.data.map(item => item.name).join(',') const paths = val.data.map(item => item.name).join(',')
const ids = val.data.map(item => item.id).join(',') const ids = val.data.map(item => item.id).join(',')
this.form["storeAddressPath"] = paths; form.value["storeAddressPath"] = paths;
this.form["storeAddressIdPath"] = ids; form.value["storeAddressIdPath"] = ids;
this.form.storeCenter = val.data[val.data.length - 1].center form.value.storeCenter = val.data[val.data.length - 1].center
}else{ }else{
this.form["storeAddressPath"] = val.data.addr; form.value["storeAddressPath"] = val.data.addr;
this.form["storeAddressIdPath"] = val.data.addrId; form.value["storeAddressIdPath"] = val.data.addrId;
this.form['storeCenter'] = val.data.position.lng + ',' + val.data.position.lat form.value['storeCenter'] = val.data.position.lng + ',' + val.data.position.lat
; ;
} }
}, }
// 获取商品分类 // 获取商品分类
getCategoryList () { function getCategoryList () {
getCategory(0).then((res) => { getCategory(0).then((res) => {
if (res.success) { if (res.success) {
this.categoryList = res.result || []; categoryList.value = res.result || [];
this.syncCategorySelection(); syncCategorySelection();
} }
}); });
}, }
normalizeCategoryId (id) { function normalizeCategoryId (id) {
return id == null ? '' : String(id); return id == null ? '' : String(id);
}, }
syncCategorySelection () { function syncCategorySelection () {
if (!Array.isArray(this.form.goodsManagementCategory)) { if (!Array.isArray(form.value.goodsManagementCategory)) {
this.form.goodsManagementCategory = []; form.value.goodsManagementCategory = [];
return; return;
} }
this.form.goodsManagementCategory = this.form.goodsManagementCategory.map( form.value.goodsManagementCategory = form.value.goodsManagementCategory.map(
(id) => this.normalizeCategoryId(id) (id) => normalizeCategoryId(id)
); );
}, }
createEmptyForm () { function createEmptyForm () {
return { return {
storeLogo: [], storeLogo: [],
goodsManagementCategory: [], goodsManagementCategory: [],
@@ -259,34 +258,32 @@ export default {
storeAddressPath: "", storeAddressPath: "",
storeCenter: "", storeCenter: "",
}; };
}, }
initFormFromContent (content) { function initFormFromContent (content) {
const src = content && Object.keys(content).length const src = content && Object.keys(content).length
? JSON.parse(JSON.stringify(content)) ? JSON.parse(JSON.stringify(content))
: {}; : {};
this.form = { form.value = {
...this.createEmptyForm(), ...createEmptyForm(),
...src, ...src,
}; };
this.form.storeLogo = src.storeLogo form.value.storeLogo = src.storeLogo
? String(src.storeLogo).split(",").filter(Boolean) ? String(src.storeLogo).split(",").filter(Boolean)
: []; : [];
this.form.goodsManagementCategory = src.goodsManagementCategory form.value.goodsManagementCategory = src.goodsManagementCategory
? String(src.goodsManagementCategory).split(",").filter(Boolean) ? String(src.goodsManagementCategory).split(",").filter(Boolean)
: []; : [];
["storeName", "storeAddressDetail", "storeDesc", "storeAddressIdPath", "storeAddressPath", "storeCenter"].forEach((key) => { ["storeName", "storeAddressDetail", "storeDesc", "storeAddressIdPath", "storeAddressPath", "storeCenter"].forEach((key) => {
if (this.form[key] == null) this.form[key] = ""; if (form.value[key] == null) form.value[key] = "";
}); });
}, }
},
created () { initFormFromContent(props.content);
this.initFormFromContent(this.content);
}, onMounted(() => {
mounted () { accessToken.accessToken = storage.getItem('accessToken');
this.accessToken.accessToken = storage.getItem('accessToken'); getCategoryList();
this.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