mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-ui.git
synced 2026-11-07 09:28:53 +08:00
refactor: 四端完成 Options API → script setup + vuex → pinia 迁移
- buyer(95) / seller(169) / im(53) / manager(280) 共 597 个组件全部从 Options API 转换为 <script setup> - 四端 vuex 全部移除,状态管理统一迁移至 pinia 4(stores/ 目录) - util.js 等基础设施去除 vm 首参依赖;全局属性($Message/$filters 等)在 script 内改为具名导入 - 被外部 ref 访问的公共组件均已补齐 defineExpose(清单见各端 MIGRATION-VUE3.md) - 各端 eslint 启用 vue/component-api-style 防止新代码回退 Options API - 四端 yarn build 全部通过;迁移约定与注意事项已更新至各端 MIGRATION-VUE3.md
This commit is contained in:
@@ -46,120 +46,117 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, ref } from "vue";
|
||||
import { getShopListData } from "@/api/shops.js";
|
||||
|
||||
export default {
|
||||
props: ["closeShop"],
|
||||
data() {
|
||||
return {
|
||||
month: "",
|
||||
selectedWay: {
|
||||
title: "过去7天",
|
||||
selected: true,
|
||||
searchType: "LAST_SEVEN",
|
||||
},
|
||||
storeId: "",
|
||||
dates: [],
|
||||
params: {
|
||||
pageNumber: 1,
|
||||
pageSize: 100,
|
||||
storeName: "",
|
||||
},
|
||||
dateList: [
|
||||
{ title: "今天", selected: false, searchType: "TODAY" },
|
||||
{ title: "昨天", selected: false, searchType: "YESTERDAY" },
|
||||
{ title: "过去7天", selected: true, searchType: "LAST_SEVEN" },
|
||||
{ title: "过去30天", selected: false, searchType: "LAST_THIRTY" },
|
||||
],
|
||||
originDateList: [
|
||||
{ title: "今天", selected: false, searchType: "TODAY" },
|
||||
{ title: "昨天", selected: false, searchType: "YESTERDAY" },
|
||||
{ title: "过去7天", selected: true, searchType: "LAST_SEVEN" },
|
||||
{ title: "过去30天", selected: false, searchType: "LAST_THIRTY" },
|
||||
],
|
||||
shopTotal: 0,
|
||||
shopsData: [],
|
||||
defineProps(["closeShop"]);
|
||||
const emit = defineEmits(["selected"]);
|
||||
|
||||
const month = ref("");
|
||||
const selectedWay = ref({
|
||||
title: "过去7天",
|
||||
selected: true,
|
||||
searchType: "LAST_SEVEN",
|
||||
});
|
||||
const storeId = ref("");
|
||||
const dates = ref([]);
|
||||
const params = ref({
|
||||
pageNumber: 1,
|
||||
pageSize: 100,
|
||||
storeName: "",
|
||||
});
|
||||
const dateList = ref([
|
||||
{ title: "今天", selected: false, searchType: "TODAY" },
|
||||
{ title: "昨天", selected: false, searchType: "YESTERDAY" },
|
||||
{ title: "过去7天", selected: true, searchType: "LAST_SEVEN" },
|
||||
{ title: "过去30天", selected: false, searchType: "LAST_THIRTY" },
|
||||
]);
|
||||
const originDateList = ref([
|
||||
{ title: "今天", selected: false, searchType: "TODAY" },
|
||||
{ title: "昨天", selected: false, searchType: "YESTERDAY" },
|
||||
{ title: "过去7天", selected: true, searchType: "LAST_SEVEN" },
|
||||
{ title: "过去30天", selected: false, searchType: "LAST_THIRTY" },
|
||||
]);
|
||||
const shopTotal = ref(0);
|
||||
const shopsData = ref([]);
|
||||
|
||||
onMounted(() => {
|
||||
getFiveYears();
|
||||
getShopList();
|
||||
});
|
||||
|
||||
function getShopList() {
|
||||
getShopListData(params.value).then((res) => {
|
||||
if (res.success) {
|
||||
shopTotal.value = res.result.total;
|
||||
shopsData.value = res.result.records || [];
|
||||
}
|
||||
});
|
||||
}
|
||||
function changeshop() {
|
||||
selectedWay.value.storeId = storeId.value;
|
||||
emit("selected", selectedWay.value);
|
||||
}
|
||||
function getFiveYears() {
|
||||
const getYear = new Date().getFullYear();
|
||||
const lastFiveYear = getYear - 5;
|
||||
const maxMonth = new Date().getMonth() + 1;
|
||||
const list = [];
|
||||
for (let year = lastFiveYear; year <= getYear; year++) {
|
||||
for (let month = 1; month <= 12; month++) {
|
||||
if (year === getYear && month > maxMonth) {
|
||||
continue;
|
||||
}
|
||||
list.push({ year, month });
|
||||
}
|
||||
}
|
||||
dates.value = list.reverse();
|
||||
}
|
||||
function changeSelect(e) {
|
||||
month.value = e;
|
||||
if (month.value) {
|
||||
dateList.value.forEach((res) => {
|
||||
res.selected = false;
|
||||
});
|
||||
const parts = String(month.value).split("-");
|
||||
selectedWay.value = {
|
||||
...selectedWay.value,
|
||||
year: parts[0],
|
||||
month: parts[1],
|
||||
searchType: "",
|
||||
storeId: storeId.value,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.getFiveYears();
|
||||
this.getShopList();
|
||||
},
|
||||
methods: {
|
||||
getShopList() {
|
||||
getShopListData(this.params).then((res) => {
|
||||
if (res.success) {
|
||||
this.shopTotal = res.result.total;
|
||||
this.shopsData = res.result.records || [];
|
||||
}
|
||||
});
|
||||
},
|
||||
changeshop() {
|
||||
this.selectedWay.storeId = this.storeId;
|
||||
this.$emit("selected", this.selectedWay);
|
||||
},
|
||||
getFiveYears() {
|
||||
const getYear = new Date().getFullYear();
|
||||
const lastFiveYear = getYear - 5;
|
||||
const maxMonth = new Date().getMonth() + 1;
|
||||
const dates = [];
|
||||
for (let year = lastFiveYear; year <= getYear; year++) {
|
||||
for (let month = 1; month <= 12; month++) {
|
||||
if (year === getYear && month > maxMonth) {
|
||||
continue;
|
||||
}
|
||||
dates.push({ year, month });
|
||||
}
|
||||
}
|
||||
this.dates = dates.reverse();
|
||||
},
|
||||
changeSelect(e) {
|
||||
this.month = e;
|
||||
if (this.month) {
|
||||
this.dateList.forEach((res) => {
|
||||
res.selected = false;
|
||||
});
|
||||
const parts = String(this.month).split("-");
|
||||
this.selectedWay = {
|
||||
...this.selectedWay,
|
||||
year: parts[0],
|
||||
month: parts[1],
|
||||
searchType: "",
|
||||
storeId: this.storeId,
|
||||
};
|
||||
this.$emit("selected", this.selectedWay);
|
||||
} else {
|
||||
const current =
|
||||
this.dateList.find((item) => item.selected) ||
|
||||
this.dateList.find((item) => item.title === this.selectedWay.title) ||
|
||||
this.dateList.find((item) => item.searchType === "LAST_SEVEN");
|
||||
this.clickBreadcrumb(current);
|
||||
}
|
||||
},
|
||||
clickBreadcrumb(item) {
|
||||
if (!item) return;
|
||||
let currentIndex;
|
||||
this.dateList.forEach((res, index) => {
|
||||
res.selected = false;
|
||||
if (res.title === item.title) {
|
||||
currentIndex = index;
|
||||
}
|
||||
});
|
||||
item.selected = true;
|
||||
item.storeId = this.storeId;
|
||||
this.month = "";
|
||||
if (item.searchType === "") {
|
||||
const origin = this.originDateList[currentIndex];
|
||||
item.searchType = (origin && origin.searchType) || "LAST_SEVEN";
|
||||
}
|
||||
this.selectedWay = item;
|
||||
this.selectedWay.year = new Date().getFullYear();
|
||||
this.selectedWay.month = "";
|
||||
this.$emit("selected", this.selectedWay);
|
||||
},
|
||||
},
|
||||
};
|
||||
emit("selected", selectedWay.value);
|
||||
} else {
|
||||
const current =
|
||||
dateList.value.find((item) => item.selected) ||
|
||||
dateList.value.find((item) => item.title === selectedWay.value.title) ||
|
||||
dateList.value.find((item) => item.searchType === "LAST_SEVEN");
|
||||
clickBreadcrumb(current);
|
||||
}
|
||||
}
|
||||
function clickBreadcrumb(item) {
|
||||
if (!item) return;
|
||||
let currentIndex;
|
||||
dateList.value.forEach((res, index) => {
|
||||
res.selected = false;
|
||||
if (res.title === item.title) {
|
||||
currentIndex = index;
|
||||
}
|
||||
});
|
||||
item.selected = true;
|
||||
item.storeId = storeId.value;
|
||||
month.value = "";
|
||||
if (item.searchType === "") {
|
||||
const origin = originDateList.value[currentIndex];
|
||||
item.searchType = (origin && origin.searchType) || "LAST_SEVEN";
|
||||
}
|
||||
selectedWay.value = item;
|
||||
selectedWay.value.year = new Date().getFullYear();
|
||||
selectedWay.value.month = "";
|
||||
emit("selected", selectedWay.value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -63,120 +63,120 @@
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, watch } from "vue";
|
||||
import { getDistributionMemberPage } from "@/api/distribution";
|
||||
import { Message } from "@/utils/message";
|
||||
|
||||
export default {
|
||||
name: "distributionSelector",
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
excludeId: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
defineOptions({ name: "distributionSelector" });
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
emits: ["update:modelValue", "confirm"],
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
mobile: "",
|
||||
nickname: "",
|
||||
},
|
||||
data: [],
|
||||
total: 0,
|
||||
selectedId: "",
|
||||
selectedRow: null,
|
||||
};
|
||||
excludeId: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
watch: {
|
||||
modelValue(val) {
|
||||
if (val) {
|
||||
this.resetState();
|
||||
this.getDataList();
|
||||
});
|
||||
const emit = defineEmits(["update:modelValue", "confirm"]);
|
||||
|
||||
const loading = ref(false);
|
||||
// searchForm 在 resetState 中被整体替换,用 ref 而非 reactive
|
||||
const searchForm = ref({
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
mobile: "",
|
||||
nickname: "",
|
||||
});
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
const selectedId = ref("");
|
||||
const selectedRow = ref(null);
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
if (val) {
|
||||
resetState();
|
||||
getDataList();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
function resetState() {
|
||||
searchForm.value = {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
mobile: "",
|
||||
nickname: "",
|
||||
};
|
||||
selectedId.value = "";
|
||||
selectedRow.value = null;
|
||||
}
|
||||
function buildParams() {
|
||||
const params = {
|
||||
pageNumber: searchForm.value.pageNumber,
|
||||
pageSize: searchForm.value.pageSize,
|
||||
distributionStatus: "PASS",
|
||||
};
|
||||
if (searchForm.value.mobile) params.mobile = searchForm.value.mobile;
|
||||
if (searchForm.value.nickname) params.nickname = searchForm.value.nickname;
|
||||
return params;
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
getDistributionMemberPage(buildParams())
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
if (!res.success) return;
|
||||
const result = res.result || {};
|
||||
let records = result.records || [];
|
||||
const beforeCount = records.length;
|
||||
if (props.excludeId) {
|
||||
records = records.filter((item) => item.id !== props.excludeId);
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
resetState() {
|
||||
this.searchForm = {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
mobile: "",
|
||||
nickname: "",
|
||||
};
|
||||
this.selectedId = "";
|
||||
this.selectedRow = null;
|
||||
},
|
||||
buildParams() {
|
||||
const params = {
|
||||
pageNumber: this.searchForm.pageNumber,
|
||||
pageSize: this.searchForm.pageSize,
|
||||
distributionStatus: "PASS",
|
||||
};
|
||||
if (this.searchForm.mobile) params.mobile = this.searchForm.mobile;
|
||||
if (this.searchForm.nickname) params.nickname = this.searchForm.nickname;
|
||||
return params;
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
getDistributionMemberPage(this.buildParams())
|
||||
.then((res) => {
|
||||
this.loading = false;
|
||||
if (!res.success) return;
|
||||
const result = res.result || {};
|
||||
let records = result.records || [];
|
||||
const beforeCount = records.length;
|
||||
if (this.excludeId) {
|
||||
records = records.filter((item) => item.id !== this.excludeId);
|
||||
}
|
||||
this.data = records;
|
||||
const excludedCount = beforeCount - records.length;
|
||||
this.total = Math.max(0, (result.total || 0) - excludedCount);
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getDataList();
|
||||
},
|
||||
changePageSize() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getDataList();
|
||||
},
|
||||
handleRowClick(row) {
|
||||
if (!row) return;
|
||||
this.selectedRow = row;
|
||||
this.selectedId = row.id;
|
||||
},
|
||||
handleCurrentChange(row) {
|
||||
if (!row) return;
|
||||
this.selectedRow = row;
|
||||
this.selectedId = row.id;
|
||||
},
|
||||
handleRadioChange(row) {
|
||||
this.selectedRow = row;
|
||||
},
|
||||
handleClose() {
|
||||
this.$emit("update:modelValue", false);
|
||||
},
|
||||
handleConfirm() {
|
||||
if (!this.selectedRow) {
|
||||
this.$Message.warning("请选择分销员");
|
||||
return;
|
||||
}
|
||||
this.$emit("confirm", this.selectedRow);
|
||||
this.handleClose();
|
||||
},
|
||||
},
|
||||
};
|
||||
data.value = records;
|
||||
const excludedCount = beforeCount - records.length;
|
||||
total.value = Math.max(0, (result.total || 0) - excludedCount);
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.value.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
function changePageSize() {
|
||||
searchForm.value.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
function handleRowClick(row) {
|
||||
if (!row) return;
|
||||
selectedRow.value = row;
|
||||
selectedId.value = row.id;
|
||||
}
|
||||
function handleCurrentChange(row) {
|
||||
if (!row) return;
|
||||
selectedRow.value = row;
|
||||
selectedId.value = row.id;
|
||||
}
|
||||
function handleRadioChange(row) {
|
||||
selectedRow.value = row;
|
||||
}
|
||||
function handleClose() {
|
||||
emit("update:modelValue", false);
|
||||
}
|
||||
function handleConfirm() {
|
||||
if (!selectedRow.value) {
|
||||
Message.warning("请选择分销员");
|
||||
return;
|
||||
}
|
||||
emit("confirm", selectedRow.value);
|
||||
handleClose();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -8,132 +8,142 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
<script setup>
|
||||
import {
|
||||
computed,
|
||||
nextTick,
|
||||
onActivated,
|
||||
onDeactivated,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
ref,
|
||||
watch,
|
||||
} from "vue";
|
||||
import { initEditor } from "@/components/editor/config";
|
||||
import uploadImage from "@/components/editor/upload-image.vue";
|
||||
export default {
|
||||
components:{uploadImage},
|
||||
name: "Tinymce",
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
height:{
|
||||
type:String,
|
||||
default:'500px'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
editorContent() {
|
||||
return this.modelValue ?? this.value ?? "";
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 引入编辑器的配置
|
||||
initEditor,
|
||||
hasChange: false, // 标记内容是否有更改
|
||||
hasInit: false, // 标记编辑器是否已初始化
|
||||
tinymceId:
|
||||
"tinymce-" + +new Date() + ((Math.random() * 1000).toFixed(0) + ""), // 生成唯一的编辑器 ID
|
||||
fullscreen: false, // 标记编辑器是否处于全屏模式
|
||||
toolbar: [], // 工具栏配置
|
||||
content: "", // 编辑器内容
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.init();
|
||||
},
|
||||
watch: {
|
||||
editorContent: {
|
||||
handler(val) {
|
||||
if (!this.hasChange && this.hasInit) {
|
||||
// 当内容有更改且编辑器已初始化时,更新编辑器的内容
|
||||
this.$nextTick(() => {
|
||||
const editor = window.tinymce?.get(this.tinymceId);
|
||||
if (editor) editor.setContent(val || "");
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
emitContent(content) {
|
||||
this.$emit("update:modelValue", content);
|
||||
this.$emit("input", content);
|
||||
},
|
||||
// 数据返回并给富文本框插入图片
|
||||
insertImage(arr){
|
||||
arr.forEach(v => window.tinymce.get(this.tinymceId).insertContent(`<img src="${v}" >`))
|
||||
},
|
||||
init() {
|
||||
// 初始化编辑器
|
||||
this.initTinymce();
|
||||
},
|
||||
initTinymce() {
|
||||
const _this = this;
|
||||
window.tinymce.init({
|
||||
selector: `#${this.tinymceId}`,
|
||||
convert_urls: false,
|
||||
init_instance_callback: (editor) => {
|
||||
if (_this.editorContent) {
|
||||
// 如果有初始值,则设置编辑器的内容为初始值
|
||||
_this.$nextTick(() => editor.setContent(_this.editorContent));
|
||||
}
|
||||
_this.hasInit = true;
|
||||
// 监听编辑器内容的变化
|
||||
editor.on("NodeChange Change KeyUp SetContent", () => {
|
||||
_this.hasChange = true;
|
||||
// Vue3 v-model 使用 update:modelValue,同时保留 input 兼容旧用法
|
||||
_this.emitContent(editor.getContent());
|
||||
});
|
||||
},
|
||||
setup(editor) {
|
||||
// 监听全屏状态变化
|
||||
editor.on("FullscreenStateChanged", (e) => {
|
||||
_this.fullscreen = e.state;
|
||||
});
|
||||
},
|
||||
..._this.initEditor,
|
||||
|
||||
height:this.height
|
||||
defineOptions({ name: "Tinymce" });
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: "500px",
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(["update:modelValue", "input"]);
|
||||
|
||||
// 引入编辑器的配置(data 中的 initEditor 与 import 同名,直接使用 import)
|
||||
const hasChange = ref(false); // 标记内容是否有更改
|
||||
const hasInit = ref(false); // 标记编辑器是否已初始化
|
||||
const tinymceId =
|
||||
"tinymce-" + +new Date() + ((Math.random() * 1000).toFixed(0) + ""); // 生成唯一的编辑器 ID
|
||||
const fullscreen = ref(false); // 标记编辑器是否处于全屏模式
|
||||
const toolbar = ref([]); // 工具栏配置
|
||||
const content = ref(""); // 编辑器内容
|
||||
|
||||
const editorContent = computed(() => {
|
||||
return props.modelValue ?? props.value ?? "";
|
||||
});
|
||||
|
||||
watch(editorContent, (val) => {
|
||||
if (!hasChange.value && hasInit.value) {
|
||||
// 当内容有更改且编辑器已初始化时,更新编辑器的内容
|
||||
nextTick(() => {
|
||||
const editor = window.tinymce?.get(tinymceId);
|
||||
if (editor) editor.setContent(val || "");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function emitContent(content) {
|
||||
emit("update:modelValue", content);
|
||||
emit("input", content);
|
||||
}
|
||||
// 数据返回并给富文本框插入图片
|
||||
function insertImage(arr) {
|
||||
arr.forEach((v) =>
|
||||
window.tinymce.get(tinymceId).insertContent(`<img src="${v}" >`)
|
||||
);
|
||||
}
|
||||
function init() {
|
||||
// 初始化编辑器
|
||||
initTinymce();
|
||||
}
|
||||
function initTinymce() {
|
||||
window.tinymce.init({
|
||||
selector: `#${tinymceId}`,
|
||||
convert_urls: false,
|
||||
init_instance_callback: (editor) => {
|
||||
if (editorContent.value) {
|
||||
// 如果有初始值,则设置编辑器的内容为初始值
|
||||
nextTick(() => editor.setContent(editorContent.value));
|
||||
}
|
||||
hasInit.value = true;
|
||||
// 监听编辑器内容的变化
|
||||
editor.on("NodeChange Change KeyUp SetContent", () => {
|
||||
hasChange.value = true;
|
||||
// Vue3 v-model 使用 update:modelValue,同时保留 input 兼容旧用法
|
||||
emitContent(editor.getContent());
|
||||
});
|
||||
},
|
||||
setContent(value) {
|
||||
// 设置编辑器的内容
|
||||
window.tinymce.get(this.tinymceId).setContent(value);
|
||||
setup(editor) {
|
||||
// 监听全屏状态变化
|
||||
editor.on("FullscreenStateChanged", (e) => {
|
||||
fullscreen.value = e.state;
|
||||
});
|
||||
},
|
||||
getContent() {
|
||||
// 获取编辑器的内容
|
||||
return window.tinymce.get(this.tinymceId).getContent();
|
||||
},
|
||||
destroyTinymce() {
|
||||
const tinymce = window.tinymce.get(this.tinymceId);
|
||||
...initEditor,
|
||||
|
||||
if (tinymce) {
|
||||
// 销毁编辑器实例
|
||||
tinymce.destroy();
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
activated() {
|
||||
if (window.tinymce) {
|
||||
this.initTinymce();
|
||||
}
|
||||
},
|
||||
deactivated() {
|
||||
this.destroyTinymce();
|
||||
},
|
||||
unmounted() {
|
||||
this.destroyTinymce();
|
||||
},
|
||||
};
|
||||
height: props.height,
|
||||
});
|
||||
}
|
||||
function getEditor() {
|
||||
// 获取 tinymce 实例(原组件无此方法,为对齐 seller 版暴露能力而新增)
|
||||
return window.tinymce?.get(tinymceId);
|
||||
}
|
||||
function setContent(value) {
|
||||
// 设置编辑器的内容
|
||||
window.tinymce.get(tinymceId).setContent(value);
|
||||
}
|
||||
function getContent() {
|
||||
// 获取编辑器的内容
|
||||
return window.tinymce.get(tinymceId).getContent();
|
||||
}
|
||||
function destroyTinymce() {
|
||||
const tinymce = window.tinymce.get(tinymceId);
|
||||
|
||||
if (tinymce) {
|
||||
// 销毁编辑器实例
|
||||
tinymce.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
// created 钩子:顶层直接执行(保留原 created + mounted 双重 init 的历史行为)
|
||||
init();
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
onActivated(() => {
|
||||
if (window.tinymce) {
|
||||
initTinymce();
|
||||
}
|
||||
});
|
||||
onDeactivated(() => {
|
||||
destroyTinymce();
|
||||
});
|
||||
onUnmounted(() => {
|
||||
destroyTinymce();
|
||||
});
|
||||
|
||||
// 父组件通过 ref 调用:articleList 使用 getContent;setContent/getEditor 对齐 seller 版暴露能力
|
||||
defineExpose({ getEditor, setContent, getContent });
|
||||
</script>
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
</vuedraggable>
|
||||
<div class="upload-box">
|
||||
<el-upload
|
||||
ref="upload"
|
||||
ref="uploadRef"
|
||||
:action="uploadFileUrl"
|
||||
:headers="accessToken"
|
||||
:show-file-list="false"
|
||||
@@ -64,7 +64,7 @@
|
||||
@closed="resetOssSelection"
|
||||
>
|
||||
<OssManage
|
||||
ref="ossManage"
|
||||
ref="ossManageRef"
|
||||
:is-component="true"
|
||||
:initialize="showOssManager"
|
||||
:hide-select-footer="true"
|
||||
@@ -84,123 +84,120 @@
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
<script setup>
|
||||
import { nextTick, onMounted, ref } from "vue";
|
||||
import { Delete, Plus, ZoomIn } from "@element-plus/icons-vue";
|
||||
import vuedraggable from "vuedraggable";
|
||||
import { uploadFile } from "@/libs/axios";
|
||||
import { getStore } from "@/libs/storage";
|
||||
import { Message } from "@/utils/message";
|
||||
import OssManage from "@/views/sys/oss-manage/ossManage.vue";
|
||||
|
||||
export default {
|
||||
name: "upload-image",
|
||||
components: {
|
||||
OssManage,
|
||||
vuedraggable,
|
||||
Delete,
|
||||
Plus,
|
||||
ZoomIn,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
uploadFileUrl: uploadFile,
|
||||
accessToken: {},
|
||||
showOssManager: false,
|
||||
images: [],
|
||||
selectedImage: [],
|
||||
viewImage: false,
|
||||
previewUrl: "",
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.accessToken = {
|
||||
accessToken: this.getStore("accessToken"),
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
draggableItemKey(item) {
|
||||
return item?.url || item;
|
||||
},
|
||||
handleClickUploadImage() {
|
||||
this.show = true;
|
||||
},
|
||||
handleView(url) {
|
||||
this.previewUrl = url;
|
||||
this.viewImage = true;
|
||||
},
|
||||
callback() {
|
||||
const formatImages = this.images.map((item) => item.url);
|
||||
this.$emit("callback", formatImages);
|
||||
this.show = false;
|
||||
},
|
||||
handleRemoveGoodsPicture(__index) {
|
||||
this.images.splice(__index, 1);
|
||||
},
|
||||
handleBeforeUpload(file) {
|
||||
const okType = ["image/jpeg", "image/png", "image/jpg"].includes(file.type);
|
||||
if (!okType) {
|
||||
this.$Message.warning("文件 " + file.name + " 的格式不正确,请选择 jpg/jpeg/png");
|
||||
return false;
|
||||
}
|
||||
if (file.size / 1024 / 1024 > 10) {
|
||||
this.$Message.warning("图片大小不能超过10MB");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
handleSuccessGoodsPicture(res) {
|
||||
const url = res?.result ?? res?.data?.result;
|
||||
if (url) {
|
||||
this.images.push({ url });
|
||||
} else {
|
||||
this.$Message.error(res?.message || "上传失败");
|
||||
}
|
||||
},
|
||||
handleUploadError(err) {
|
||||
this.$Message.error(err?.message || String(err));
|
||||
},
|
||||
confirmUrls() {
|
||||
this.applySelectedImages();
|
||||
this.showOssManager = false;
|
||||
},
|
||||
cancelOssImport() {
|
||||
this.selectedImage = [];
|
||||
this.showOssManager = false;
|
||||
},
|
||||
resetOssSelection() {
|
||||
this.selectedImage = [];
|
||||
},
|
||||
handleOssSelected(list) {
|
||||
this.selectedImage = Array.isArray(list) ? list : [];
|
||||
},
|
||||
parseOssSelectionUrl(item) {
|
||||
if (!item) {
|
||||
return "";
|
||||
}
|
||||
if (typeof item === "string") {
|
||||
const index = item.indexOf(",");
|
||||
return index >= 0 ? item.slice(index + 1) : item;
|
||||
}
|
||||
return item.url || "";
|
||||
},
|
||||
applySelectedImages() {
|
||||
(this.selectedImage || []).forEach((item) => {
|
||||
const url = this.parseOssSelectionUrl(item);
|
||||
if (url) {
|
||||
this.images.push({ url });
|
||||
}
|
||||
});
|
||||
},
|
||||
importOSS() {
|
||||
this.selectedImage = [];
|
||||
this.showOssManager = true;
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.ossManage) {
|
||||
this.$refs.ossManage.selectImage = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
defineOptions({ name: "upload-image" });
|
||||
|
||||
const emit = defineEmits(["callback"]);
|
||||
|
||||
const show = ref(false);
|
||||
const uploadFileUrl = uploadFile;
|
||||
const accessToken = ref({});
|
||||
const showOssManager = ref(false);
|
||||
const images = ref([]);
|
||||
const selectedImage = ref([]);
|
||||
const viewImage = ref(false);
|
||||
const previewUrl = ref("");
|
||||
|
||||
// 模板 ref:upload/ossManage 原名与顶层变量无冲突,但统一改名避免与后续变量互相覆盖
|
||||
const uploadRef = ref(null);
|
||||
const ossManageRef = ref(null);
|
||||
|
||||
onMounted(() => {
|
||||
accessToken.value = {
|
||||
accessToken: getStore("accessToken"),
|
||||
};
|
||||
});
|
||||
|
||||
function draggableItemKey(item) {
|
||||
return item?.url || item;
|
||||
}
|
||||
function handleClickUploadImage() {
|
||||
show.value = true;
|
||||
}
|
||||
function handleView(url) {
|
||||
previewUrl.value = url;
|
||||
viewImage.value = true;
|
||||
}
|
||||
function callback() {
|
||||
const formatImages = images.value.map((item) => item.url);
|
||||
emit("callback", formatImages);
|
||||
show.value = false;
|
||||
}
|
||||
function handleRemoveGoodsPicture(__index) {
|
||||
images.value.splice(__index, 1);
|
||||
}
|
||||
function handleBeforeUpload(file) {
|
||||
const okType = ["image/jpeg", "image/png", "image/jpg"].includes(file.type);
|
||||
if (!okType) {
|
||||
Message.warning("文件 " + file.name + " 的格式不正确,请选择 jpg/jpeg/png");
|
||||
return false;
|
||||
}
|
||||
if (file.size / 1024 / 1024 > 10) {
|
||||
Message.warning("图片大小不能超过10MB");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function handleSuccessGoodsPicture(res) {
|
||||
const url = res?.result ?? res?.data?.result;
|
||||
if (url) {
|
||||
images.value.push({ url });
|
||||
} else {
|
||||
Message.error(res?.message || "上传失败");
|
||||
}
|
||||
}
|
||||
function handleUploadError(err) {
|
||||
Message.error(err?.message || String(err));
|
||||
}
|
||||
function confirmUrls() {
|
||||
applySelectedImages();
|
||||
showOssManager.value = false;
|
||||
}
|
||||
function cancelOssImport() {
|
||||
selectedImage.value = [];
|
||||
showOssManager.value = false;
|
||||
}
|
||||
function resetOssSelection() {
|
||||
selectedImage.value = [];
|
||||
}
|
||||
function handleOssSelected(list) {
|
||||
selectedImage.value = Array.isArray(list) ? list : [];
|
||||
}
|
||||
function parseOssSelectionUrl(item) {
|
||||
if (!item) {
|
||||
return "";
|
||||
}
|
||||
if (typeof item === "string") {
|
||||
const index = item.indexOf(",");
|
||||
return index >= 0 ? item.slice(index + 1) : item;
|
||||
}
|
||||
return item.url || "";
|
||||
}
|
||||
function applySelectedImages() {
|
||||
(selectedImage.value || []).forEach((item) => {
|
||||
const url = parseOssSelectionUrl(item);
|
||||
if (url) {
|
||||
images.value.push({ url });
|
||||
}
|
||||
});
|
||||
}
|
||||
function importOSS() {
|
||||
selectedImage.value = [];
|
||||
showOssManager.value = true;
|
||||
nextTick(() => {
|
||||
if (ossManageRef.value) {
|
||||
ossManageRef.value.selectImage = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
:key="index"
|
||||
:index="index"
|
||||
:setting="zone"
|
||||
:ref="`zone${index}`"
|
||||
:ref="(el) => setZoneRef(el, index)"
|
||||
@delItem="removeItem($event)"
|
||||
@changeInfo="changeInfo($event)"
|
||||
></zone>
|
||||
@@ -62,160 +62,188 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, onMounted, getCurrentInstance } from "vue";
|
||||
import Zone from "./Zone";
|
||||
import addItem from "../directives/addItem";
|
||||
import vAddItem from "../directives/addItem";
|
||||
|
||||
export default {
|
||||
name: "HotZone",
|
||||
data() {
|
||||
return {
|
||||
zones: [],
|
||||
};
|
||||
defineOptions({ name: "HotZone" });
|
||||
|
||||
const props = defineProps({
|
||||
image: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
props: {
|
||||
image: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
zonesInit: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
max: {
|
||||
type: Number,
|
||||
},
|
||||
zonesInit: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
mounted() {
|
||||
this.zones = this.zonesInit.concat();
|
||||
max: {
|
||||
type: Number,
|
||||
},
|
||||
methods: {
|
||||
async addHotzone() {
|
||||
let perInfo = {
|
||||
topPer: 0.15,
|
||||
leftPer: 0.3,
|
||||
widthPer: 0.2,
|
||||
heightPer: 0.2,
|
||||
img: "",
|
||||
link: "",
|
||||
type: "",
|
||||
title: "",
|
||||
};
|
||||
let images = await this.getImageSize(this.image);
|
||||
if (images) {
|
||||
if (images.height >= 1000) {
|
||||
perInfo.heightPer = this.convertNumberToDecimal(images.height) / (images.height / 1000);
|
||||
} else {
|
||||
perInfo.heightPer = this.convertNumberToDecimal(images.height);
|
||||
}
|
||||
perInfo.widthPer = this.convertNumberToDecimal(images.width) / 2;
|
||||
}
|
||||
this.addItem(perInfo);
|
||||
},
|
||||
convertNumberToDecimal(num) {
|
||||
if (num >= 10000) {
|
||||
return num / 100000;
|
||||
} else if (num >= 1000) {
|
||||
return num / 10000;
|
||||
} else if (num >= 100) {
|
||||
return num / 1000;
|
||||
} else if (num >= 10) {
|
||||
return num / 100;
|
||||
}
|
||||
},
|
||||
getImageSize(url) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
let image = new Image();
|
||||
image.onload = function () {
|
||||
resolve({
|
||||
width: image.width,
|
||||
height: image.height,
|
||||
});
|
||||
};
|
||||
image.onerror = function () {
|
||||
reject(new Error("error"));
|
||||
};
|
||||
image.src = url;
|
||||
});
|
||||
const emit = defineEmits([
|
||||
"choose",
|
||||
"erase",
|
||||
"overRange",
|
||||
"remove",
|
||||
"add",
|
||||
"change",
|
||||
]);
|
||||
|
||||
const zones = ref([]);
|
||||
const zoneRefs = ref([]);
|
||||
const instance = getCurrentInstance();
|
||||
|
||||
onMounted(() => {
|
||||
zones.value = props.zonesInit.concat();
|
||||
});
|
||||
|
||||
// v-for 内的动态 ref:按 index 收集 Zone 组件实例
|
||||
function setZoneRef(el, index) {
|
||||
if (el) zoneRefs.value[index] = el;
|
||||
}
|
||||
|
||||
async function addHotzone() {
|
||||
let perInfo = {
|
||||
topPer: 0.15,
|
||||
leftPer: 0.3,
|
||||
widthPer: 0.2,
|
||||
heightPer: 0.2,
|
||||
img: "",
|
||||
link: "",
|
||||
type: "",
|
||||
title: "",
|
||||
};
|
||||
let images = await getImageSize(props.image);
|
||||
if (images) {
|
||||
if (images.height >= 1000) {
|
||||
perInfo.heightPer = convertNumberToDecimal(images.height) / (images.height / 1000);
|
||||
} else {
|
||||
perInfo.heightPer = convertNumberToDecimal(images.height);
|
||||
}
|
||||
perInfo.widthPer = convertNumberToDecimal(images.width) / 2;
|
||||
}
|
||||
addItem(perInfo);
|
||||
}
|
||||
|
||||
function convertNumberToDecimal(num) {
|
||||
if (num >= 10000) {
|
||||
return num / 100000;
|
||||
} else if (num >= 1000) {
|
||||
return num / 10000;
|
||||
} else if (num >= 100) {
|
||||
return num / 1000;
|
||||
} else if (num >= 10) {
|
||||
return num / 100;
|
||||
}
|
||||
}
|
||||
|
||||
function getImageSize(url) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
let image = new Image();
|
||||
image.onload = function () {
|
||||
resolve({
|
||||
width: image.width,
|
||||
height: image.height,
|
||||
});
|
||||
},
|
||||
editZone(index) {
|
||||
this.$refs[`zone${index}`][0].showModalFn(index);
|
||||
},
|
||||
delZone(index) {
|
||||
this.$refs[`zone${index}`][0].delItem(index);
|
||||
},
|
||||
showZoneText(zone) {
|
||||
switch (zone.type) {
|
||||
case "goods":
|
||||
return `商品:${zone.goodsName}`;
|
||||
case "category":
|
||||
return `分类:${zone.name}`;
|
||||
case "shops":
|
||||
return `店铺:${zone.storeName}`;
|
||||
case "pages":
|
||||
return `文章:${zone.title}`;
|
||||
case "marketing":
|
||||
return `促销活动商品:${zone.goodsName}`;
|
||||
case "other":
|
||||
return `${zone.title}`;
|
||||
case "special":
|
||||
return `专题:${zone.title}`;
|
||||
default:
|
||||
return "请选择跳转链接";
|
||||
}
|
||||
},
|
||||
changeInfo(res) {
|
||||
let { info, index, zoneInfo } = res;
|
||||
info = { ...zoneInfo, ...info };
|
||||
// 改变热区并发送change通知
|
||||
Object.assign(this.zones[index], info);
|
||||
this.hasChange("changeInfo");
|
||||
this.$forceUpdate();
|
||||
},
|
||||
addItem(setting) {
|
||||
this.zones.push(setting);
|
||||
this.$emit("choose");
|
||||
// this.hasChange() 不应该发送通知,mouseup判定成功才应该发
|
||||
// this.$emit('add', setting)
|
||||
},
|
||||
eraseItem(index = this.zones.length - 1) {
|
||||
this.zones.splice(index, 1);
|
||||
this.$emit("erase", index);
|
||||
},
|
||||
isOverRange() {
|
||||
let { max, zones } = this;
|
||||
};
|
||||
image.onerror = function () {
|
||||
reject(new Error("error"));
|
||||
};
|
||||
image.src = url;
|
||||
});
|
||||
}
|
||||
|
||||
return max && zones.length > max;
|
||||
},
|
||||
overRange() {
|
||||
const index = this.zones.length - 1;
|
||||
function editZone(index) {
|
||||
zoneRefs.value[index].showModalFn(index);
|
||||
}
|
||||
|
||||
this.zones.splice(index, 1);
|
||||
this.$emit("overRange", index);
|
||||
},
|
||||
removeItem(index = this.zones.length - 1) {
|
||||
this.zones.splice(index, 1);
|
||||
this.hasChange("removeItem");
|
||||
this.$emit("remove", index);
|
||||
},
|
||||
changeItem(info, isAdd) {
|
||||
const index = this.zones.length - 1;
|
||||
// 改变热区并发送change通知
|
||||
Object.assign(this.zones[index], info);
|
||||
this.hasChange("changeItem");
|
||||
isAdd && this.$emit("add", this.zones[index]);
|
||||
},
|
||||
hasChange(from) {
|
||||
this.$emit("change", this.zones);
|
||||
},
|
||||
},
|
||||
directives: {
|
||||
addItem,
|
||||
},
|
||||
components: {
|
||||
Zone,
|
||||
},
|
||||
};
|
||||
function delZone(index) {
|
||||
zoneRefs.value[index].delItem(index);
|
||||
}
|
||||
|
||||
function showZoneText(zone) {
|
||||
switch (zone.type) {
|
||||
case "goods":
|
||||
return `商品:${zone.goodsName}`;
|
||||
case "category":
|
||||
return `分类:${zone.name}`;
|
||||
case "shops":
|
||||
return `店铺:${zone.storeName}`;
|
||||
case "pages":
|
||||
return `文章:${zone.title}`;
|
||||
case "marketing":
|
||||
return `促销活动商品:${zone.goodsName}`;
|
||||
case "other":
|
||||
return `${zone.title}`;
|
||||
case "special":
|
||||
return `专题:${zone.title}`;
|
||||
default:
|
||||
return "请选择跳转链接";
|
||||
}
|
||||
}
|
||||
|
||||
function changeInfo(res) {
|
||||
let { info, index, zoneInfo } = res;
|
||||
info = { ...zoneInfo, ...info };
|
||||
// 改变热区并发送change通知
|
||||
Object.assign(zones.value[index], info);
|
||||
hasChange("changeInfo");
|
||||
instance.proxy.$forceUpdate();
|
||||
}
|
||||
|
||||
function addItem(setting) {
|
||||
zones.value.push(setting);
|
||||
emit("choose");
|
||||
// this.hasChange() 不应该发送通知,mouseup判定成功才应该发
|
||||
// this.$emit('add', setting)
|
||||
}
|
||||
|
||||
function eraseItem(index = zones.value.length - 1) {
|
||||
zones.value.splice(index, 1);
|
||||
emit("erase", index);
|
||||
}
|
||||
|
||||
function isOverRange() {
|
||||
return props.max && zones.value.length > props.max;
|
||||
}
|
||||
|
||||
function overRange() {
|
||||
const index = zones.value.length - 1;
|
||||
|
||||
zones.value.splice(index, 1);
|
||||
emit("overRange", index);
|
||||
}
|
||||
|
||||
function removeItem(index = zones.value.length - 1) {
|
||||
zones.value.splice(index, 1);
|
||||
hasChange("removeItem");
|
||||
emit("remove", index);
|
||||
}
|
||||
|
||||
function changeItem(info, isAdd) {
|
||||
const index = zones.value.length - 1;
|
||||
// 改变热区并发送change通知
|
||||
Object.assign(zones.value[index], info);
|
||||
hasChange("changeItem");
|
||||
isAdd && emit("add", zones.value[index]);
|
||||
}
|
||||
|
||||
function hasChange(from) {
|
||||
emit("change", zones.value);
|
||||
}
|
||||
|
||||
// v-add-item 指令通过 binding.instance 调用以下方法/读取 zones;
|
||||
// Zone 的 v-drag-item / v-change-size 指令通过 ctx.$parent.zones 读取 zones
|
||||
defineExpose({
|
||||
zones,
|
||||
addItem,
|
||||
eraseItem,
|
||||
isOverRange,
|
||||
overRange,
|
||||
changeItem,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -86,171 +86,177 @@
|
||||
@callback="callbackSelected"
|
||||
:isComponent="true"
|
||||
:initialize="picModelFlag"
|
||||
ref="ossManage"
|
||||
ref="ossManageRef"
|
||||
/>
|
||||
<template #footer />
|
||||
</el-dialog>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import changeSize from "../directives/changeSize";
|
||||
import dragItem from "../directives/dragItem";
|
||||
<script setup>
|
||||
import { ref, reactive, watch, nextTick, onMounted } from "vue";
|
||||
import vChangeSize from "../directives/changeSize";
|
||||
import vDragItem from "../directives/dragItem";
|
||||
import ossManage from "@/views/sys/oss-manage/ossManage";
|
||||
import { formatLinkType } from "@/utils/filters";
|
||||
|
||||
export default {
|
||||
name: "Zone",
|
||||
components: {
|
||||
ossManage,
|
||||
},
|
||||
defineOptions({ name: "Zone" });
|
||||
|
||||
data() {
|
||||
return {
|
||||
zoneTop: "",
|
||||
zoneLeft: "",
|
||||
zoneWidth: "",
|
||||
zoneHeight: "",
|
||||
hideZone: false,
|
||||
tooSmall: false,
|
||||
showModal: false,
|
||||
picModelFlag: false,
|
||||
currentIndex: 0,
|
||||
currentShowIndex: -1,
|
||||
zoneForm: {
|
||||
img: "",
|
||||
link: "",
|
||||
type: "",
|
||||
},
|
||||
};
|
||||
},
|
||||
props: ["index", "setting"],
|
||||
mounted() {
|
||||
this.setZoneInfo(this.setting);
|
||||
},
|
||||
methods: {
|
||||
setZoneInfo(val) {
|
||||
this.zoneTop = this.getZoneStyle(val.topPer);
|
||||
this.zoneLeft = this.getZoneStyle(val.leftPer);
|
||||
this.zoneWidth = this.getZoneStyle(val.widthPer);
|
||||
this.zoneHeight = this.getZoneStyle(val.heightPer);
|
||||
this.tooSmall = val.widthPer < 0.01 && val.heightPer < 0.01;
|
||||
this.zoneForm.link = val.link;
|
||||
this.settingZone(val);
|
||||
},
|
||||
handlehideZone(isHide = true) {
|
||||
if (this.hideZone === isHide) {
|
||||
return;
|
||||
}
|
||||
const props = defineProps(["index", "setting"]);
|
||||
const emit = defineEmits(["changeInfo", "delItem"]);
|
||||
|
||||
this.hideZone = isHide;
|
||||
},
|
||||
changeInfo(info = {}) {
|
||||
const { index } = this;
|
||||
const zoneTop = ref("");
|
||||
const zoneLeft = ref("");
|
||||
const zoneWidth = ref("");
|
||||
const zoneHeight = ref("");
|
||||
const hideZone = ref(false);
|
||||
const tooSmall = ref(false);
|
||||
const showModal = ref(false);
|
||||
const picModelFlag = ref(false);
|
||||
const currentIndex = ref(0);
|
||||
const currentShowIndex = ref(-1);
|
||||
const zoneForm = reactive({
|
||||
img: "",
|
||||
link: "",
|
||||
type: "",
|
||||
});
|
||||
|
||||
this.$emit("changeInfo", {
|
||||
info,
|
||||
index,
|
||||
zoneInfo: this.zoneForm,
|
||||
});
|
||||
},
|
||||
showModalFn(index) {
|
||||
this.showModal = true;
|
||||
this.currentIndex = index;
|
||||
},
|
||||
// 选择图片
|
||||
handleSelectImg() {
|
||||
this.picModelFlag = true;
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.ossManage) {
|
||||
this.$refs.ossManage.selectImage = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
// 选择图片回调
|
||||
callbackSelected(item) {
|
||||
this.picModelFlag = false;
|
||||
this.zoneForm.img = item.url;
|
||||
},
|
||||
// 未在原 data 中声明、直接挂载在 this 上的遗留字段(仅赋值,未被读取)
|
||||
let selectedNav;
|
||||
|
||||
// 调起选择链接弹窗
|
||||
handleSelectLink(item, index) {
|
||||
if (item) this.selectedNav = item;
|
||||
this.$refs.liliDialog.open("link");
|
||||
},
|
||||
// 已选链接
|
||||
selectedLink(val) {
|
||||
this.zoneForm.link = this.$filters.formatLinkType(val);
|
||||
this.settingZone(val);
|
||||
this.changeInfo(this.zoneForm);
|
||||
},
|
||||
settingZone(val) {
|
||||
this.zoneForm.type = val.___type || val.type;
|
||||
this.zoneForm.title = val.title;
|
||||
if(val.pageType == "special"){
|
||||
this.zoneForm.id = val.id;
|
||||
this.zoneForm.pageType = val.pageType
|
||||
this.zoneForm.type = val.pageType
|
||||
this.zoneForm.title = val.name
|
||||
}
|
||||
switch (val.___type) {
|
||||
case "goods":
|
||||
this.zoneForm.id = val.id;
|
||||
this.zoneForm.goodsId = val.goodsId;
|
||||
this.zoneForm.goodsName = val.goodsName;
|
||||
break;
|
||||
case "category":
|
||||
this.zoneForm.id = val.allId;
|
||||
this.zoneForm.name = val.name;
|
||||
break;
|
||||
case "shops":
|
||||
this.zoneForm.id = val.id;
|
||||
this.zoneForm.storeName = val.storeName;
|
||||
break;
|
||||
case "pages":
|
||||
this.zoneForm.id = val.id;
|
||||
this.zoneForm.___path = val.___path;
|
||||
this.zoneForm.title = val.title;
|
||||
break;
|
||||
case "marketing":
|
||||
this.zoneForm.id = val.id;
|
||||
this.zoneForm.goodsId = val.goodsId;
|
||||
this.zoneForm.goodsName = val.goodsName;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
saveZone() {
|
||||
this.showModal = false;
|
||||
this.changeInfo(this.zoneForm);
|
||||
},
|
||||
cancelZone() {
|
||||
this.showModal = false;
|
||||
},
|
||||
delZone() {
|
||||
this.delItem(this.currentIndex);
|
||||
},
|
||||
delItem(index) {
|
||||
this.$emit("delItem", index);
|
||||
},
|
||||
getZoneStyle(val) {
|
||||
return `${(val || 0) * 100}%`;
|
||||
},
|
||||
},
|
||||
const liliDialog = ref(null);
|
||||
// 模板 ref 与 import 组件同名会互相覆盖,故改名 ossManageRef
|
||||
const ossManageRef = ref(null);
|
||||
|
||||
watch: {
|
||||
setting: {
|
||||
handler: function (val) {
|
||||
this.setZoneInfo(val);
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
onMounted(() => {
|
||||
setZoneInfo(props.setting);
|
||||
});
|
||||
|
||||
function setZoneInfo(val) {
|
||||
zoneTop.value = getZoneStyle(val.topPer);
|
||||
zoneLeft.value = getZoneStyle(val.leftPer);
|
||||
zoneWidth.value = getZoneStyle(val.widthPer);
|
||||
zoneHeight.value = getZoneStyle(val.heightPer);
|
||||
tooSmall.value = val.widthPer < 0.01 && val.heightPer < 0.01;
|
||||
zoneForm.link = val.link;
|
||||
settingZone(val);
|
||||
}
|
||||
function handlehideZone(isHide = true) {
|
||||
if (hideZone.value === isHide) {
|
||||
return;
|
||||
}
|
||||
|
||||
hideZone.value = isHide;
|
||||
}
|
||||
function changeInfo(info = {}) {
|
||||
const index = props.index;
|
||||
|
||||
emit("changeInfo", {
|
||||
info,
|
||||
index,
|
||||
zoneInfo: zoneForm,
|
||||
});
|
||||
}
|
||||
function showModalFn(index) {
|
||||
showModal.value = true;
|
||||
currentIndex.value = index;
|
||||
}
|
||||
// 选择图片
|
||||
function handleSelectImg() {
|
||||
picModelFlag.value = true;
|
||||
nextTick(() => {
|
||||
if (ossManageRef.value) {
|
||||
ossManageRef.value.selectImage = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
// 选择图片回调
|
||||
function callbackSelected(item) {
|
||||
picModelFlag.value = false;
|
||||
zoneForm.img = item.url;
|
||||
}
|
||||
|
||||
// 调起选择链接弹窗
|
||||
function handleSelectLink(item, index) {
|
||||
if (item) selectedNav = item;
|
||||
liliDialog.value.open("link");
|
||||
}
|
||||
// 已选链接
|
||||
function selectedLink(val) {
|
||||
zoneForm.link = formatLinkType(val);
|
||||
settingZone(val);
|
||||
changeInfo(zoneForm);
|
||||
}
|
||||
function settingZone(val) {
|
||||
zoneForm.type = val.___type || val.type;
|
||||
zoneForm.title = val.title;
|
||||
if(val.pageType == "special"){
|
||||
zoneForm.id = val.id;
|
||||
zoneForm.pageType = val.pageType
|
||||
zoneForm.type = val.pageType
|
||||
zoneForm.title = val.name
|
||||
}
|
||||
switch (val.___type) {
|
||||
case "goods":
|
||||
zoneForm.id = val.id;
|
||||
zoneForm.goodsId = val.goodsId;
|
||||
zoneForm.goodsName = val.goodsName;
|
||||
break;
|
||||
case "category":
|
||||
zoneForm.id = val.allId;
|
||||
zoneForm.name = val.name;
|
||||
break;
|
||||
case "shops":
|
||||
zoneForm.id = val.id;
|
||||
zoneForm.storeName = val.storeName;
|
||||
break;
|
||||
case "pages":
|
||||
zoneForm.id = val.id;
|
||||
zoneForm.___path = val.___path;
|
||||
zoneForm.title = val.title;
|
||||
break;
|
||||
case "marketing":
|
||||
zoneForm.id = val.id;
|
||||
zoneForm.goodsId = val.goodsId;
|
||||
zoneForm.goodsName = val.goodsName;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
function saveZone() {
|
||||
showModal.value = false;
|
||||
changeInfo(zoneForm);
|
||||
}
|
||||
function cancelZone() {
|
||||
showModal.value = false;
|
||||
}
|
||||
function delZone() {
|
||||
delItem(currentIndex.value);
|
||||
}
|
||||
function delItem(index) {
|
||||
emit("delItem", index);
|
||||
}
|
||||
function getZoneStyle(val) {
|
||||
return `${(val || 0) * 100}%`;
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.setting,
|
||||
(val) => {
|
||||
setZoneInfo(val);
|
||||
},
|
||||
directives: {
|
||||
changeSize,
|
||||
dragItem,
|
||||
},
|
||||
};
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 外部访问:父组件 Hotzone.vue 通过 $refs 调用 showModalFn/delItem;
|
||||
// 自定义指令 changeSize/dragItem 通过 binding.instance 调用 handlehideZone/changeInfo
|
||||
defineExpose({
|
||||
showModalFn,
|
||||
delItem,
|
||||
handlehideZone,
|
||||
changeInfo,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
>
|
||||
<template v-if="flag && hotzoneRes">
|
||||
<hotzone
|
||||
ref="hotzone"
|
||||
ref="hotzoneRef"
|
||||
@change="changeHotzone"
|
||||
:zonesInit="hotzoneRes.zoneInfo"
|
||||
:image="hotzoneRes.img"
|
||||
@@ -22,47 +22,50 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import hotzone from "./components/Hotzone.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
hotzone,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
flag: false,
|
||||
hotzoneRes: null,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
changeHotzone(info) {
|
||||
if (this.hotzoneRes) {
|
||||
this.hotzoneRes.zoneInfo = info;
|
||||
}
|
||||
this.$emit("changeZone", info);
|
||||
},
|
||||
clickClose() {
|
||||
this.flag = false;
|
||||
this.hotzoneRes = null;
|
||||
this.$emit("closeFlag", false);
|
||||
},
|
||||
clickOK() {
|
||||
this.clickClose();
|
||||
},
|
||||
open(val) {
|
||||
this.hotzoneRes = val || null;
|
||||
if (this.hotzoneRes && !this.hotzoneRes.zoneInfo) {
|
||||
this.hotzoneRes.zoneInfo = [];
|
||||
}
|
||||
this.flag = true;
|
||||
},
|
||||
close() {
|
||||
this.flag = false;
|
||||
this.hotzoneRes = null;
|
||||
},
|
||||
},
|
||||
};
|
||||
const emit = defineEmits(["changeZone", "closeFlag"]);
|
||||
|
||||
const flag = ref(false);
|
||||
const hotzoneRes = ref(null);
|
||||
|
||||
// hotzoneRef:模板 ref 与组件 import 同名,改名避免互相覆盖
|
||||
const hotzoneRef = ref(null);
|
||||
|
||||
function changeHotzone(info) {
|
||||
if (hotzoneRes.value) {
|
||||
hotzoneRes.value.zoneInfo = info;
|
||||
}
|
||||
emit("changeZone", info);
|
||||
}
|
||||
|
||||
function clickClose() {
|
||||
flag.value = false;
|
||||
hotzoneRes.value = null;
|
||||
emit("closeFlag", false);
|
||||
}
|
||||
|
||||
function clickOK() {
|
||||
clickClose();
|
||||
}
|
||||
|
||||
function open(val) {
|
||||
hotzoneRes.value = val || null;
|
||||
if (hotzoneRes.value && !hotzoneRes.value.zoneInfo) {
|
||||
hotzoneRes.value.zoneInfo = [];
|
||||
}
|
||||
flag.value = true;
|
||||
}
|
||||
|
||||
function close() {
|
||||
flag.value = false;
|
||||
hotzoneRes.value = null;
|
||||
}
|
||||
|
||||
// decorate.vue 通过 hotzoneRef.value.open(val) 调用
|
||||
defineExpose({ open, close });
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
:deep(.el-dialog) {
|
||||
|
||||
@@ -84,165 +84,165 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref, watch } from "vue";
|
||||
import * as API_Goods from "@/api/goods";
|
||||
import { goodsTypeLabel, goodsTypeTagType } from "@/constants/goodsType";
|
||||
export default {
|
||||
props: {
|
||||
selectedWay: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
type: "multiple",
|
||||
selectedList: [],
|
||||
skuList: [],
|
||||
total: 0,
|
||||
goodsParams: {
|
||||
pageNumber: 1,
|
||||
pageSize: 15,
|
||||
order: "desc",
|
||||
goodsName: "",
|
||||
sn: "",
|
||||
categoryPath: "",
|
||||
marketEnable: "UPPER",
|
||||
authFlag: "PASS",
|
||||
sort: "createTime",
|
||||
},
|
||||
category: [],
|
||||
goodsData: [],
|
||||
empty: false,
|
||||
loading: false,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
category(val) {
|
||||
this.goodsParams.categoryPath = val && val.length ? val[2] : "";
|
||||
},
|
||||
selectedWay: {
|
||||
handler(val) {
|
||||
this.selectedList = Array.isArray(val) ? val.slice() : [];
|
||||
},
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
"goodsParams.categoryPath": {
|
||||
handler() {
|
||||
this.goodsData = [];
|
||||
this.goodsParams.pageNumber = 1;
|
||||
this.getQueryGoodsList();
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
methods: {
|
||||
goodsTypeLabel,
|
||||
goodsTypeTagType,
|
||||
onSearchGoods() {
|
||||
this.goodsData = [];
|
||||
this.goodsParams.pageNumber = 1;
|
||||
this.getQueryGoodsList();
|
||||
},
|
||||
changePageSize(v) {
|
||||
this.goodsParams.pageNumber = v;
|
||||
this.getQueryGoodsList();
|
||||
},
|
||||
getQueryGoodsList() {
|
||||
this.loading = true;
|
||||
API_Goods.getGoodsSkuData(this.goodsParams)
|
||||
.then((res) => {
|
||||
this.initGoods(res);
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
initGoods(res) {
|
||||
const records = res?.result?.records || [];
|
||||
if (records.length) {
|
||||
records.forEach((item) => {
|
||||
item.selected = false;
|
||||
item.___type = "goods";
|
||||
this.selectedList.forEach((e) => {
|
||||
if (e.id && e.id === item.id) {
|
||||
item.selected = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
this.total = res.result.total || 0;
|
||||
this.goodsData = records;
|
||||
this.empty = false;
|
||||
} else {
|
||||
this.goodsData = [];
|
||||
this.empty = true;
|
||||
}
|
||||
},
|
||||
emitSelected(list) {
|
||||
this.selectedList = list;
|
||||
this.$emit("selected", this.selectedList);
|
||||
},
|
||||
init() {
|
||||
API_Goods.getGoodsSkuData(this.goodsParams).then((res) => {
|
||||
this.initGoods(res);
|
||||
});
|
||||
API_Goods.getCategoryTree({ deleteFlag: false }).then((res) => {
|
||||
if (res.success) {
|
||||
this.deepGroup(res.result);
|
||||
}
|
||||
});
|
||||
},
|
||||
deepGroup(val) {
|
||||
val.forEach((item) => {
|
||||
let childWay = [];
|
||||
if (item.children) {
|
||||
item.children.forEach((child) => {
|
||||
if (child.children) {
|
||||
child.children.forEach((grandson, index, arr) => {
|
||||
arr[index] = {
|
||||
value: grandson.id,
|
||||
label: grandson.name,
|
||||
};
|
||||
});
|
||||
}
|
||||
childWay.push({
|
||||
value: child.id,
|
||||
label: child.name,
|
||||
children: child.children,
|
||||
});
|
||||
});
|
||||
}
|
||||
this.skuList.push({
|
||||
value: item.id,
|
||||
label: item.name,
|
||||
children: childWay,
|
||||
});
|
||||
});
|
||||
},
|
||||
checkedGoods(val) {
|
||||
if (this.type !== "multiple") {
|
||||
this.goodsData.forEach((item) => {
|
||||
item.selected = false;
|
||||
});
|
||||
val.selected = true;
|
||||
this.emitSelected([val]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!val.selected) {
|
||||
val.selected = true;
|
||||
this.emitSelected([...this.selectedList, val]);
|
||||
} else {
|
||||
val.selected = false;
|
||||
this.emitSelected(this.selectedList.filter((item) => item.id !== val.id));
|
||||
}
|
||||
},
|
||||
const props = defineProps({
|
||||
selectedWay: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
};
|
||||
});
|
||||
const emit = defineEmits(["selected"]);
|
||||
|
||||
const type = ref("multiple");
|
||||
const selectedList = ref([]);
|
||||
const skuList = ref([]);
|
||||
const total = ref(0);
|
||||
const goodsParams = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 15,
|
||||
order: "desc",
|
||||
goodsName: "",
|
||||
sn: "",
|
||||
categoryPath: "",
|
||||
marketEnable: "UPPER",
|
||||
authFlag: "PASS",
|
||||
sort: "createTime",
|
||||
});
|
||||
const category = ref([]);
|
||||
const goodsData = ref([]);
|
||||
const empty = ref(false);
|
||||
const loading = ref(false);
|
||||
|
||||
watch(category, (val) => {
|
||||
goodsParams.categoryPath = val && val.length ? val[2] : "";
|
||||
});
|
||||
watch(
|
||||
() => props.selectedWay,
|
||||
(val) => {
|
||||
selectedList.value = Array.isArray(val) ? val.slice() : [];
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
watch(
|
||||
() => goodsParams.categoryPath,
|
||||
() => {
|
||||
goodsData.value = [];
|
||||
goodsParams.pageNumber = 1;
|
||||
getQueryGoodsList();
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
|
||||
function onSearchGoods() {
|
||||
goodsData.value = [];
|
||||
goodsParams.pageNumber = 1;
|
||||
getQueryGoodsList();
|
||||
}
|
||||
function changePageSize(v) {
|
||||
goodsParams.pageNumber = v;
|
||||
getQueryGoodsList();
|
||||
}
|
||||
function getQueryGoodsList() {
|
||||
loading.value = true;
|
||||
API_Goods.getGoodsSkuData(goodsParams)
|
||||
.then((res) => {
|
||||
initGoods(res);
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
function initGoods(res) {
|
||||
const records = res?.result?.records || [];
|
||||
if (records.length) {
|
||||
records.forEach((item) => {
|
||||
item.selected = false;
|
||||
item.___type = "goods";
|
||||
selectedList.value.forEach((e) => {
|
||||
if (e.id && e.id === item.id) {
|
||||
item.selected = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
total.value = res.result.total || 0;
|
||||
goodsData.value = records;
|
||||
empty.value = false;
|
||||
} else {
|
||||
goodsData.value = [];
|
||||
empty.value = true;
|
||||
}
|
||||
}
|
||||
function emitSelected(list) {
|
||||
selectedList.value = list;
|
||||
emit("selected", selectedList.value);
|
||||
}
|
||||
function init() {
|
||||
API_Goods.getGoodsSkuData(goodsParams).then((res) => {
|
||||
initGoods(res);
|
||||
});
|
||||
API_Goods.getCategoryTree({ deleteFlag: false }).then((res) => {
|
||||
if (res.success) {
|
||||
deepGroup(res.result);
|
||||
}
|
||||
});
|
||||
}
|
||||
function deepGroup(val) {
|
||||
val.forEach((item) => {
|
||||
let childWay = [];
|
||||
if (item.children) {
|
||||
item.children.forEach((child) => {
|
||||
if (child.children) {
|
||||
child.children.forEach((grandson, index, arr) => {
|
||||
arr[index] = {
|
||||
value: grandson.id,
|
||||
label: grandson.name,
|
||||
};
|
||||
});
|
||||
}
|
||||
childWay.push({
|
||||
value: child.id,
|
||||
label: child.name,
|
||||
children: child.children,
|
||||
});
|
||||
});
|
||||
}
|
||||
skuList.value.push({
|
||||
value: item.id,
|
||||
label: item.name,
|
||||
children: childWay,
|
||||
});
|
||||
});
|
||||
}
|
||||
function checkedGoods(val) {
|
||||
if (type.value !== "multiple") {
|
||||
goodsData.value.forEach((item) => {
|
||||
item.selected = false;
|
||||
});
|
||||
val.selected = true;
|
||||
emitSelected([val]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!val.selected) {
|
||||
val.selected = true;
|
||||
emitSelected([...selectedList.value, val]);
|
||||
} else {
|
||||
val.selected = false;
|
||||
emitSelected(selectedList.value.filter((item) => item.id !== val.id));
|
||||
}
|
||||
}
|
||||
|
||||
// 父组件 lili-dialog/index.vue 通过 $refs.goodsDialog.type = "single" 切换单选;
|
||||
// expose 的 ref 外部读写等价于原 data 字段
|
||||
defineExpose({ type });
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
@import "./style.scss";
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
}
|
||||
"
|
||||
v-if="goodsFlag"
|
||||
ref="goodsDialog"
|
||||
ref="goodsDialogRef"
|
||||
:selectedWay="goodsData"
|
||||
/>
|
||||
<linkDialog
|
||||
@@ -36,68 +36,82 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import goodsDialog from "./goods-dialog";
|
||||
import linkDialog from "./link-dialog";
|
||||
export default {
|
||||
components: {
|
||||
goodsDialog,
|
||||
linkDialog,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
goodsFlag: false, // 是否商品选择器
|
||||
goodsData: [], //选择的商品
|
||||
linkData: "", //选择的链接
|
||||
flag: false, // modal显隐
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
clearGoodsSelected(){
|
||||
this.goodsData = []
|
||||
},
|
||||
// 关闭弹窗
|
||||
clickClose() {
|
||||
this.flag = false;
|
||||
this.goodsFlag = false;
|
||||
this.$emit("closeFlag", false);
|
||||
},
|
||||
// 单选商品
|
||||
singleGoods() {
|
||||
var timer = setInterval(() => {
|
||||
if (this.$refs.goodsDialog) {
|
||||
this.$refs.goodsDialog.type = "single";
|
||||
clearInterval(timer);
|
||||
}
|
||||
}, 100);
|
||||
},
|
||||
// 点击确认
|
||||
clickOK() {
|
||||
if (this.goodsFlag) {
|
||||
this.$emit("selectedGoodsData", this.goodsData);
|
||||
} else {
|
||||
this.$emit("selectedLink", this.linkData);
|
||||
}
|
||||
this.clickClose();
|
||||
},
|
||||
// 打开组件方法
|
||||
open(type, mutiple) {
|
||||
this.flag = true;
|
||||
if (type == "goods") {
|
||||
this.goodsFlag = true;
|
||||
if (mutiple) {
|
||||
this.singleGoods();
|
||||
}
|
||||
} else {
|
||||
this.goodsFlag = false;
|
||||
}
|
||||
},
|
||||
// 关闭组件
|
||||
close() {
|
||||
this.flag = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const emit = defineEmits(["closeFlag", "selectedGoodsData", "selectedLink"]);
|
||||
|
||||
const goodsFlag = ref(false); // 是否商品选择器
|
||||
const goodsData = ref([]); //选择的商品
|
||||
const linkData = ref(""); //选择的链接
|
||||
const flag = ref(false); // modal显隐
|
||||
|
||||
// 模板 ref:原 ref="goodsDialog" 与 import 的组件同名,改名避免互相覆盖
|
||||
const goodsDialogRef = ref(null);
|
||||
|
||||
function clearGoodsSelected() {
|
||||
goodsData.value = [];
|
||||
}
|
||||
// 关闭弹窗
|
||||
function clickClose() {
|
||||
flag.value = false;
|
||||
goodsFlag.value = false;
|
||||
emit("closeFlag", false);
|
||||
}
|
||||
// 单选商品
|
||||
function singleGoods() {
|
||||
var timer = setInterval(() => {
|
||||
if (goodsDialogRef.value) {
|
||||
goodsDialogRef.value.type = "single";
|
||||
clearInterval(timer);
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
// 点击确认
|
||||
function clickOK() {
|
||||
if (goodsFlag.value) {
|
||||
emit("selectedGoodsData", goodsData.value);
|
||||
} else {
|
||||
emit("selectedLink", linkData.value);
|
||||
}
|
||||
clickClose();
|
||||
}
|
||||
// 打开组件方法
|
||||
function open(type, mutiple) {
|
||||
flag.value = true;
|
||||
if (type == "goods") {
|
||||
goodsFlag.value = true;
|
||||
if (mutiple) {
|
||||
singleGoods();
|
||||
}
|
||||
} else {
|
||||
goodsFlag.value = false;
|
||||
}
|
||||
}
|
||||
// 关闭组件
|
||||
function close() {
|
||||
flag.value = false;
|
||||
}
|
||||
|
||||
// 外部通过 ref 访问的成员(grep manager/src 全量确认):
|
||||
// - open:page-decoration 各 modelList、modelForm/modelFormItem、hotzone/Zone、wap/advertising、alertAdvertising 等打开弹窗(goods/link)
|
||||
// - singleGoods:modelList/forYour.vue、mixModel.vue 直接调用
|
||||
// - clearGoodsSelected:wap/decorate.vue、modelList/goodsAndType.vue 调用
|
||||
// - goodsData:modelList/newGoodsSort.vue、notEnough.vue 回写已选商品
|
||||
// - goodsFlag:wap/decorate.vue 直接写入
|
||||
// - flag:wap/decorate.vue 直接写入 true 打开弹窗
|
||||
// - close:未被 grep 到外部调用,原 Options API 实例天然暴露,保守保留
|
||||
defineExpose({
|
||||
open,
|
||||
close,
|
||||
singleGoods,
|
||||
clearGoodsSelected,
|
||||
goodsData,
|
||||
goodsFlag,
|
||||
flag,
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
:deep(.el-dialog__body) {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
:key="i"
|
||||
>
|
||||
<component
|
||||
ref="lili-component"
|
||||
ref="liliComponentRef"
|
||||
:is="templateWay[item.name]"
|
||||
@selected="
|
||||
(val) => {
|
||||
@@ -20,94 +20,85 @@
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import wap from "./wap.js";
|
||||
<script setup>
|
||||
import { nextTick, onMounted, ref, watch } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import wapList from "./wap.js";
|
||||
import goodsDialog from "./goods-dialog";
|
||||
import templateWay from "./template/index";
|
||||
export default {
|
||||
components: {
|
||||
goodsDialog
|
||||
|
||||
const emit = defineEmits(["selectedLink"]);
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const changed = ref(""); // 变更模板
|
||||
const selected = ref(0); // 已选数据
|
||||
const selectedLink = ref(""); //选中的链接
|
||||
// wap 为共享的模块级数组,保持原 data 引用同一数组的行为
|
||||
const wap = ref(wapList); // tab标签
|
||||
const activeTab = ref(wapList[0]?.title || "");
|
||||
// v-for 内模板 ref 收集为数组;原名 lili-component 含连字符,改名并同步 script
|
||||
const liliComponentRef = ref([]);
|
||||
|
||||
watch(
|
||||
changed,
|
||||
(val) => {
|
||||
emit("selectedLink", val[0]); //因为是单选,所以直接返回第一个
|
||||
},
|
||||
setup() {
|
||||
return { templateWay };
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
changed: "", // 变更模板
|
||||
selected: 0, // 已选数据
|
||||
selectedLink: "", //选中的链接
|
||||
wap, // tab标签
|
||||
activeTab: wap[0]?.title || "",
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
changed: {
|
||||
handler(val) {
|
||||
this.$emit("selectedLink", val[0]); //因为是单选,所以直接返回第一个
|
||||
},
|
||||
deep: true
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
if (liliComponentRef.value?.[0]) {
|
||||
liliComponentRef.value[0].type = "single"; //商品页面设置成为单选
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.$refs["lili-component"][0].type = "single"; //商品页面设置成为单选
|
||||
});
|
||||
let url = window.location.href
|
||||
if(url.indexOf('/floorList/renovation') != -1){
|
||||
//此处去重防止移动楼层多次点击push 太多数据
|
||||
let obj = {};
|
||||
this.wap = this.wap.reduce((cur, next) => {
|
||||
//对象去重
|
||||
if (next.title != undefined) {
|
||||
obj[next.title]
|
||||
? ""
|
||||
: (obj[next.title] = true && cur.push(next));
|
||||
}
|
||||
return cur;
|
||||
}, []);
|
||||
if (this.$route.path !== '/floorList/main') {
|
||||
this.wap.forEach((items, indexs) => {
|
||||
if (items.title == '活动') {
|
||||
this.wap.splice(indexs, 1)
|
||||
}
|
||||
})
|
||||
});
|
||||
let url = window.location.href;
|
||||
if (url.indexOf("/floorList/renovation") != -1) {
|
||||
//此处去重防止移动楼层多次点击push 太多数据
|
||||
let obj = {};
|
||||
wap.value = wap.value.reduce((cur, next) => {
|
||||
//对象去重
|
||||
if (next.title != undefined) {
|
||||
obj[next.title]
|
||||
? ""
|
||||
: (obj[next.title] = true && cur.push(next));
|
||||
}
|
||||
}else{
|
||||
this.wap.push( {
|
||||
title: "活动",
|
||||
url: "3",
|
||||
name: "marketing"
|
||||
})
|
||||
let obj = {};
|
||||
this.wap = this.wap.reduce((cur, next) => {
|
||||
//对象去重
|
||||
if (next.title != undefined) {
|
||||
obj[next.title]
|
||||
? ""
|
||||
: (obj[next.title] = true && cur.push(next));
|
||||
}
|
||||
return cur;
|
||||
}, []);
|
||||
return cur;
|
||||
}, []);
|
||||
if (route.path !== "/floorList/main") {
|
||||
wap.value.forEach((items, indexs) => {
|
||||
if (items.title == "活动") {
|
||||
wap.value.splice(indexs, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.wap.forEach((item) => {
|
||||
if (item) {
|
||||
item.selected = false;
|
||||
}
|
||||
} else {
|
||||
wap.value.push({
|
||||
title: "活动",
|
||||
url: "3",
|
||||
name: "marketing",
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
// isVisible(item) {
|
||||
// const type = this.$route.query.pagetype;
|
||||
// if (type == "INDEX" && [ "discover"].includes(item.name)) {
|
||||
// return false;
|
||||
// }else if(type == "DISCOVER" && item.name == 'special'){
|
||||
// return false;
|
||||
// }else{
|
||||
// return true
|
||||
// }
|
||||
// },
|
||||
let obj = {};
|
||||
wap.value = wap.value.reduce((cur, next) => {
|
||||
//对象去重
|
||||
if (next.title != undefined) {
|
||||
obj[next.title]
|
||||
? ""
|
||||
: (obj[next.title] = true && cur.push(next));
|
||||
}
|
||||
return cur;
|
||||
}, []);
|
||||
}
|
||||
};
|
||||
wap.value.forEach((item) => {
|
||||
if (item) {
|
||||
item.selected = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 无 defineExpose:grep manager/src 全量确认无外部 ref/$refs/$parent 访问本组件成员
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
@import "./style.scss";
|
||||
|
||||
@@ -22,94 +22,94 @@
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, ref } from "vue";
|
||||
import { getCategoryTree } from "@/api/goods.js";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
parentIndex: '', // 分类父级下标
|
||||
secondIndex: '', // 分类二级下标
|
||||
thirdIndex: '', // 分类三级下标
|
||||
categoryList: [], // 分类列表一级数据
|
||||
secondLevel: [], // 分类列表二级数据
|
||||
thirdLevel: [], // 分类列表三级数据
|
||||
selectedData: "", // 已选分类数据
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
methods: {
|
||||
loadCategoryFromCache() {
|
||||
try {
|
||||
const raw = localStorage.getItem("category");
|
||||
if (!raw) {
|
||||
return null;
|
||||
}
|
||||
const parsed = JSON.parse(raw);
|
||||
return Array.isArray(parsed) ? parsed : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
applyCategoryList(category) {
|
||||
if (!Array.isArray(category)) {
|
||||
this.categoryList = [];
|
||||
return;
|
||||
}
|
||||
category.forEach((item) => {
|
||||
item.___type = "category";
|
||||
});
|
||||
this.categoryList = category;
|
||||
},
|
||||
// 点击一级
|
||||
handleClickChild(item, index) {
|
||||
this.parentIndex = index;
|
||||
this.secondLevel = item.children || [];
|
||||
item.___type = "category";
|
||||
item.allId = item.id;
|
||||
const emit = defineEmits(["selected"]);
|
||||
|
||||
this.secondIndex = '';
|
||||
this.thirdIndex = '';
|
||||
this.thirdLevel = []
|
||||
this.$emit("selected", [item]);
|
||||
// 点击第一级的时候默认显示第二级第一个
|
||||
// this.handleClickSecondChild(item.children, 0);
|
||||
},
|
||||
// 点击二级
|
||||
handleClickSecondChild(second, index) {
|
||||
second.___type = "category";
|
||||
second.allId = `${second.parentId},${second.id}`
|
||||
// 无 defineExpose:grep manager/src 全量确认无外部 ref/$refs/$parent 访问本组件成员
|
||||
// (link-dialog.vue 的 $refs["lili-component"][0].type 写入指向首个 tab 的 goods-dialog,与本组件无关;
|
||||
// decorate.vue 等仅通过 @selected → selectedLink 事件交互)
|
||||
const parentIndex = ref(''); // 分类父级下标
|
||||
const secondIndex = ref(''); // 分类二级下标
|
||||
const thirdIndex = ref(''); // 分类三级下标
|
||||
const categoryList = ref([]); // 分类列表一级数据
|
||||
const secondLevel = ref([]); // 分类列表二级数据
|
||||
const thirdLevel = ref([]); // 分类列表三级数据
|
||||
const selectedData = ref(""); // 已选分类数据
|
||||
|
||||
this.secondIndex = index;
|
||||
this.thirdLevel = second.children || [];
|
||||
this.thirdIndex = '';
|
||||
this.$emit("selected", [second]);
|
||||
// this.handleClickthirdChild(second.children[0], 0);
|
||||
},
|
||||
// 点击三级
|
||||
handleClickthirdChild(item, index) {
|
||||
item.___type = "category";
|
||||
item.allId = `${this.categoryList[this.parentIndex].id},${item.parentId},${item.id}`
|
||||
this.$emit("selected", [item]);
|
||||
this.thirdIndex = index;
|
||||
},
|
||||
init() {
|
||||
const cached = this.loadCategoryFromCache();
|
||||
if (cached) {
|
||||
this.applyCategoryList(cached);
|
||||
return;
|
||||
}
|
||||
getCategoryTree().then((res) => {
|
||||
if (res.success && Array.isArray(res.result)) {
|
||||
localStorage.setItem("category", JSON.stringify(res.result));
|
||||
this.applyCategoryList(res.result);
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
function loadCategoryFromCache() {
|
||||
try {
|
||||
const raw = localStorage.getItem("category");
|
||||
if (!raw) {
|
||||
return null;
|
||||
}
|
||||
const parsed = JSON.parse(raw);
|
||||
return Array.isArray(parsed) ? parsed : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
function applyCategoryList(category) {
|
||||
if (!Array.isArray(category)) {
|
||||
categoryList.value = [];
|
||||
return;
|
||||
}
|
||||
category.forEach((item) => {
|
||||
item.___type = "category";
|
||||
});
|
||||
categoryList.value = category;
|
||||
}
|
||||
// 点击一级
|
||||
function handleClickChild(item, index) {
|
||||
parentIndex.value = index;
|
||||
secondLevel.value = item.children || [];
|
||||
item.___type = "category";
|
||||
item.allId = item.id;
|
||||
|
||||
secondIndex.value = '';
|
||||
thirdIndex.value = '';
|
||||
thirdLevel.value = []
|
||||
emit("selected", [item]);
|
||||
// 点击第一级的时候默认显示第二级第一个
|
||||
// handleClickSecondChild(item.children, 0);
|
||||
}
|
||||
// 点击二级
|
||||
function handleClickSecondChild(second, index) {
|
||||
second.___type = "category";
|
||||
second.allId = `${second.parentId},${second.id}`
|
||||
|
||||
secondIndex.value = index;
|
||||
thirdLevel.value = second.children || [];
|
||||
thirdIndex.value = '';
|
||||
emit("selected", [second]);
|
||||
// handleClickthirdChild(second.children[0], 0);
|
||||
}
|
||||
// 点击三级
|
||||
function handleClickthirdChild(item, index) {
|
||||
item.___type = "category";
|
||||
item.allId = `${categoryList.value[parentIndex.value].id},${item.parentId},${item.id}`
|
||||
emit("selected", [item]);
|
||||
thirdIndex.value = index;
|
||||
}
|
||||
function init() {
|
||||
const cached = loadCategoryFromCache();
|
||||
if (cached) {
|
||||
applyCategoryList(cached);
|
||||
return;
|
||||
}
|
||||
getCategoryTree().then((res) => {
|
||||
if (res.success && Array.isArray(res.result)) {
|
||||
localStorage.setItem("category", JSON.stringify(res.result));
|
||||
applyCategoryList(res.result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.list {
|
||||
|
||||
@@ -68,99 +68,95 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
<script setup>
|
||||
import { computed, onMounted, ref, watch } from "vue";
|
||||
import { getAllPromotion } from "@/api/promotion";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
totals: "",
|
||||
loading: true,
|
||||
promotionList: "",
|
||||
selectedIndex: 0,
|
||||
promotions: "",
|
||||
index: 999,
|
||||
params: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
},
|
||||
showPromotionList: [],
|
||||
};
|
||||
const emit = defineEmits(["selected"]);
|
||||
|
||||
const totals = ref("");
|
||||
const loading = ref(true);
|
||||
const promotionList = ref("");
|
||||
const selectedIndex = ref(0);
|
||||
const promotions = ref("");
|
||||
const index = ref(999);
|
||||
const params = ref({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
});
|
||||
const showPromotionList = ref([]);
|
||||
|
||||
const isSeckillMode = computed(() => {
|
||||
return promotions.value === "SECKILL";
|
||||
});
|
||||
|
||||
watch(
|
||||
params,
|
||||
() => {
|
||||
index.value = 999;
|
||||
typeOption(promotions.value) && typeOption(promotions.value).methodsed();
|
||||
},
|
||||
computed: {
|
||||
isSeckillMode() {
|
||||
return this.promotions === "SECKILL";
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
watch: {
|
||||
params: {
|
||||
handler() {
|
||||
this.index = 999;
|
||||
this.typeOption(this.promotions) && this.typeOption(this.promotions).methodsed();
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
seckillStatusText(v) {
|
||||
if (v === "APPLY") return "申请";
|
||||
if (v === "PASS") return "通过";
|
||||
return "拒绝";
|
||||
},
|
||||
sortGoods(type) {
|
||||
this.loading = false;
|
||||
this.showPromotionList = this.promotionList[type];
|
||||
},
|
||||
typeOption(type) {
|
||||
switch (type) {
|
||||
case "FULL_DISCOUNT":
|
||||
return { title: "满减", methodsed: () => { this.showPromotionList = []; this.sortGoods("FULL_DISCOUNT"); } };
|
||||
case "PINTUAN":
|
||||
return { title: "拼团", methodsed: () => { this.showPromotionList = []; this.sortGoods("PINTUAN"); } };
|
||||
case "KANJIA":
|
||||
return { title: "砍价", methodsed: () => { this.showPromotionList = []; this.sortGoods("KANJIA"); } };
|
||||
case "SECKILL":
|
||||
return { title: "秒杀", methodsed: () => { this.showPromotionList = []; this.sortGoods("SECKILL"); } };
|
||||
case "POINTS_GOODS":
|
||||
return { title: "积分商品", methodsed: () => { this.showPromotionList = []; this.sortGoods("POINTS_GOODS"); } };
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
},
|
||||
selectedPromotion(val) {
|
||||
val.row.___type = "marketing";
|
||||
val.row.___promotion = this.promotions;
|
||||
this.$emit("selected", [val.row]);
|
||||
this.index = val.index;
|
||||
},
|
||||
async init() {
|
||||
const res = await getAllPromotion();
|
||||
if (res.success) {
|
||||
this.loading = false;
|
||||
this.getPromotion(res);
|
||||
} else {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
getPromotion(res) {
|
||||
if (res.result) {
|
||||
this.promotionList = res.result;
|
||||
delete this.promotionList.COUPON;
|
||||
Object.keys(res.result)[0] && this.typeOption(Object.keys(res.result)[0]).methodsed();
|
||||
this.promotions = Object.keys(res.result)[0];
|
||||
}
|
||||
},
|
||||
clickPromotion(val, i) {
|
||||
this.promotions = val;
|
||||
this.selectedIndex = i;
|
||||
this.params.pageNumber = 1;
|
||||
this.typeOption(val) && this.typeOption(val).methodsed(this.promotionList[val].id);
|
||||
},
|
||||
},
|
||||
};
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
function seckillStatusText(v) {
|
||||
if (v === "APPLY") return "申请";
|
||||
if (v === "PASS") return "通过";
|
||||
return "拒绝";
|
||||
}
|
||||
function sortGoods(type) {
|
||||
loading.value = false;
|
||||
showPromotionList.value = promotionList.value[type];
|
||||
}
|
||||
function typeOption(type) {
|
||||
switch (type) {
|
||||
case "FULL_DISCOUNT":
|
||||
return { title: "满减", methodsed: () => { showPromotionList.value = []; sortGoods("FULL_DISCOUNT"); } };
|
||||
case "PINTUAN":
|
||||
return { title: "拼团", methodsed: () => { showPromotionList.value = []; sortGoods("PINTUAN"); } };
|
||||
case "KANJIA":
|
||||
return { title: "砍价", methodsed: () => { showPromotionList.value = []; sortGoods("KANJIA"); } };
|
||||
case "SECKILL":
|
||||
return { title: "秒杀", methodsed: () => { showPromotionList.value = []; sortGoods("SECKILL"); } };
|
||||
case "POINTS_GOODS":
|
||||
return { title: "积分商品", methodsed: () => { showPromotionList.value = []; sortGoods("POINTS_GOODS"); } };
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
function selectedPromotion(val) {
|
||||
val.row.___type = "marketing";
|
||||
val.row.___promotion = promotions.value;
|
||||
emit("selected", [val.row]);
|
||||
index.value = val.index;
|
||||
}
|
||||
async function init() {
|
||||
const res = await getAllPromotion();
|
||||
if (res.success) {
|
||||
loading.value = false;
|
||||
getPromotion(res);
|
||||
} else {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
function getPromotion(res) {
|
||||
if (res.result) {
|
||||
promotionList.value = res.result;
|
||||
delete promotionList.value.COUPON;
|
||||
Object.keys(res.result)[0] && typeOption(Object.keys(res.result)[0]).methodsed();
|
||||
promotions.value = Object.keys(res.result)[0];
|
||||
}
|
||||
}
|
||||
function clickPromotion(val, i) {
|
||||
promotions.value = val;
|
||||
selectedIndex.value = i;
|
||||
params.value.pageNumber = 1;
|
||||
typeOption(val) && typeOption(val).methodsed(promotionList.value[val].id);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.link-text {
|
||||
|
||||
@@ -33,8 +33,9 @@
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { markRaw } from "vue";
|
||||
<script setup>
|
||||
import { markRaw, ref } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import {
|
||||
House,
|
||||
ShoppingCart,
|
||||
@@ -50,61 +51,61 @@ import {
|
||||
Link,
|
||||
} from "@element-plus/icons-vue";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
linkList: [
|
||||
{ title: "首页", icon: markRaw(House), ___type: "home" },
|
||||
{ title: "购物车", icon: markRaw(ShoppingCart), ___type: "cart" },
|
||||
{ title: "收藏商品", icon: markRaw(Star), ___type: "collection" },
|
||||
{ title: "我的订单", icon: markRaw(Document), ___type: "order" },
|
||||
{ title: "个人中心", icon: markRaw(User), ___type: "user" },
|
||||
{ title: "拼团频道", icon: markRaw(Promotion), ___type: "group" },
|
||||
{ title: "秒杀频道", icon: markRaw(Promotion), ___type: "seckill" },
|
||||
{ title: "领券中心", icon: markRaw(PriceTag), ___type: "coupon" },
|
||||
{ title: "签到", icon: markRaw(Sunny), ___type: "sign" },
|
||||
{ title: "小程序直播", icon: markRaw(VideoCamera), ___type: "live" },
|
||||
{ title: "砍价", icon: markRaw(Share), ___type: "kanjia" },
|
||||
{ title: "积分商城", icon: markRaw(ShoppingBag), ___type: "point" },
|
||||
],
|
||||
linkItem: {
|
||||
title: "外部链接",
|
||||
icon: markRaw(Link),
|
||||
___type: "link",
|
||||
url: "",
|
||||
},
|
||||
linkVisible: true,
|
||||
selectedIndex: 9999999,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
const urls = window.location.href;
|
||||
if (urls.indexOf("/floorList/renovation") != -1) {
|
||||
this.linkList = this.linkList.filter(
|
||||
(item) =>
|
||||
item.title !== "砍价" &&
|
||||
item.title !== "小程序直播" &&
|
||||
item.title !== "积分商城"
|
||||
);
|
||||
this.linkVisible = true;
|
||||
} else {
|
||||
this.linkVisible = false;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showLinkItem(item) {
|
||||
return (
|
||||
(item.title !== "拼团频道" && item.title !== "签到") ||
|
||||
this.$route.name !== "renovation"
|
||||
);
|
||||
},
|
||||
handleLink(val, index) {
|
||||
val = { ...val, ___type: "other" };
|
||||
this.selectedIndex = index;
|
||||
this.$emit("selected", [val]);
|
||||
},
|
||||
},
|
||||
};
|
||||
const emit = defineEmits(["selected"]);
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const linkList = ref([
|
||||
{ title: "首页", icon: markRaw(House), ___type: "home" },
|
||||
{ title: "购物车", icon: markRaw(ShoppingCart), ___type: "cart" },
|
||||
{ title: "收藏商品", icon: markRaw(Star), ___type: "collection" },
|
||||
{ title: "我的订单", icon: markRaw(Document), ___type: "order" },
|
||||
{ title: "个人中心", icon: markRaw(User), ___type: "user" },
|
||||
{ title: "拼团频道", icon: markRaw(Promotion), ___type: "group" },
|
||||
{ title: "秒杀频道", icon: markRaw(Promotion), ___type: "seckill" },
|
||||
{ title: "领券中心", icon: markRaw(PriceTag), ___type: "coupon" },
|
||||
{ title: "签到", icon: markRaw(Sunny), ___type: "sign" },
|
||||
{ title: "小程序直播", icon: markRaw(VideoCamera), ___type: "live" },
|
||||
{ title: "砍价", icon: markRaw(Share), ___type: "kanjia" },
|
||||
{ title: "积分商城", icon: markRaw(ShoppingBag), ___type: "point" },
|
||||
]);
|
||||
const linkItem = ref({
|
||||
title: "外部链接",
|
||||
icon: markRaw(Link),
|
||||
___type: "link",
|
||||
url: "",
|
||||
});
|
||||
const linkVisible = ref(true);
|
||||
const selectedIndex = ref(9999999);
|
||||
|
||||
// 原 created 钩子
|
||||
const urls = window.location.href;
|
||||
if (urls.indexOf("/floorList/renovation") != -1) {
|
||||
linkList.value = linkList.value.filter(
|
||||
(item) =>
|
||||
item.title !== "砍价" &&
|
||||
item.title !== "小程序直播" &&
|
||||
item.title !== "积分商城"
|
||||
);
|
||||
linkVisible.value = true;
|
||||
} else {
|
||||
linkVisible.value = false;
|
||||
}
|
||||
|
||||
function showLinkItem(item) {
|
||||
return (
|
||||
(item.title !== "拼团频道" && item.title !== "签到") ||
|
||||
route.name !== "renovation"
|
||||
);
|
||||
}
|
||||
function handleLink(val, index) {
|
||||
val = { ...val, ___type: "other" };
|
||||
selectedIndex.value = index;
|
||||
emit("selected", [val]);
|
||||
}
|
||||
|
||||
// 无 defineExpose:grep manager/src 全量确认无外部 ref/$refs/$parent 访问本组件成员
|
||||
// (link-dialog.vue 的 $refs["lili-component"][0].type 写入指向首个 tab 的 goods-dialog,与本组件无关)
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "../style.scss";
|
||||
|
||||
@@ -12,25 +12,20 @@
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
<script setup>
|
||||
import Article from "@/views/page/article-manage/articleList.vue";
|
||||
export default {
|
||||
components: {
|
||||
Article,
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
methods: {
|
||||
callbackArticle(val) {
|
||||
val.___type = "pages";
|
||||
|
||||
val.___path = "/pages/passport/article";
|
||||
const emit = defineEmits(["selected"]);
|
||||
|
||||
this.$emit("selected", [val]);
|
||||
},
|
||||
},
|
||||
};
|
||||
// 无 defineExpose:grep manager/src 全量确认无外部 ref/$refs/$parent 访问本组件成员
|
||||
// (link-dialog.vue 的 $refs["lili-component"][0].type 写入指向首个 tab 的 goods-dialog,与本组件无关)
|
||||
function callbackArticle(val) {
|
||||
val.___type = "pages";
|
||||
|
||||
val.___path = "/pages/passport/article";
|
||||
|
||||
emit("selected", [val]);
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-card__body) {
|
||||
|
||||
@@ -56,53 +56,56 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, reactive } from "vue";
|
||||
import { getShopListData } from "@/api/shops.js";
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
total: 0,
|
||||
params: {
|
||||
pageNumber: 1,
|
||||
pageSize: 12,
|
||||
storeDisable: "OPEN",
|
||||
storeName: "",
|
||||
},
|
||||
shopsData: [],
|
||||
selected: 9999999999,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.init();
|
||||
},
|
||||
methods: {
|
||||
resetSearch() {
|
||||
this.shopsData = [];
|
||||
this.params.pageNumber = 1;
|
||||
this.init();
|
||||
},
|
||||
changePageSize(v) {
|
||||
this.params.pageNumber = v;
|
||||
this.init();
|
||||
},
|
||||
init() {
|
||||
this.loading = true;
|
||||
getShopListData(this.params).then((res) => {
|
||||
if (res.success) {
|
||||
this.total = res.result.total;
|
||||
this.shopsData = res.result.records;
|
||||
}
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
clickShop(val, i) {
|
||||
this.selected = i;
|
||||
val = { ...val, ___type: "shops" };
|
||||
this.$emit("selected", [val]);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const emit = defineEmits(["selected"]);
|
||||
|
||||
const loading = ref(false);
|
||||
const total = ref(0);
|
||||
const params = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 12,
|
||||
storeDisable: "OPEN",
|
||||
storeName: "",
|
||||
});
|
||||
const shopsData = ref([]);
|
||||
const selected = ref(9999999999);
|
||||
|
||||
// 无 defineExpose:grep manager/src 全量确认无外部 ref/$refs/$parent 访问本组件成员
|
||||
// (link-dialog.vue 的 $refs["lili-component"][0].type 写入指向首个 tab 的 goods-dialog,与本组件无关)
|
||||
|
||||
// created
|
||||
init();
|
||||
|
||||
function resetSearch() {
|
||||
shopsData.value = [];
|
||||
params.pageNumber = 1;
|
||||
init();
|
||||
}
|
||||
|
||||
function changePageSize(v) {
|
||||
params.pageNumber = v;
|
||||
init();
|
||||
}
|
||||
|
||||
function init() {
|
||||
loading.value = true;
|
||||
getShopListData(params).then((res) => {
|
||||
if (res.success) {
|
||||
total.value = res.result.total;
|
||||
shopsData.value = res.result.records;
|
||||
}
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function clickShop(val, i) {
|
||||
selected.value = i;
|
||||
val = { ...val, ___type: "shops" };
|
||||
emit("selected", [val]);
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "../style.scss";
|
||||
|
||||
@@ -34,56 +34,57 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { getHomeList } from "@/api/other.js";
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
index: 999,
|
||||
data: [],
|
||||
params: {
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
pageClientType: "H5",
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
pageType: "SPECIAL",
|
||||
},
|
||||
total: 0,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
methods: {
|
||||
selectRow(row, idx) {
|
||||
this.index = idx;
|
||||
const payload = { ...row, pageType: "special", ___type: "special" };
|
||||
this.$emit("selected", [payload]);
|
||||
},
|
||||
changePageNum(val) {
|
||||
this.params.pageNumber = val;
|
||||
this.init();
|
||||
},
|
||||
changePageSize(val) {
|
||||
this.params.pageNumber = 1;
|
||||
this.params.pageSize = val;
|
||||
this.init();
|
||||
},
|
||||
async init() {
|
||||
this.params.pageClientType = this.$route.name === "renovation" ? "PC" : "H5";
|
||||
const res = await getHomeList(this.params);
|
||||
if (res.success) {
|
||||
this.loading = false;
|
||||
this.data = res.result.records;
|
||||
this.total = res.result.total;
|
||||
} else {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const emit = defineEmits(["selected"]);
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const loading = ref(true);
|
||||
const index = ref(999);
|
||||
const data = ref([]);
|
||||
const params = reactive({
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
pageClientType: "H5",
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
pageType: "SPECIAL",
|
||||
});
|
||||
const total = ref(0);
|
||||
|
||||
function selectRow(row, idx) {
|
||||
index.value = idx;
|
||||
const payload = { ...row, pageType: "special", ___type: "special" };
|
||||
emit("selected", [payload]);
|
||||
}
|
||||
function changePageNum(val) {
|
||||
params.pageNumber = val;
|
||||
init();
|
||||
}
|
||||
function changePageSize(val) {
|
||||
params.pageNumber = 1;
|
||||
params.pageSize = val;
|
||||
init();
|
||||
}
|
||||
async function init() {
|
||||
params.pageClientType = route.name === "renovation" ? "PC" : "H5";
|
||||
const res = await getHomeList(params);
|
||||
if (res.success) {
|
||||
loading.value = false;
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
} else {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
img {
|
||||
|
||||
@@ -6,13 +6,10 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { Loading } from "@element-plus/icons-vue";
|
||||
|
||||
export default {
|
||||
name: "circleLoading",
|
||||
components: { Loading },
|
||||
};
|
||||
defineOptions({ name: "circleLoading" });
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -6,23 +6,22 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "configDrawer",
|
||||
data() {
|
||||
return {
|
||||
drawer: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
open() {
|
||||
this.drawer = true;
|
||||
},
|
||||
close() {
|
||||
this.drawer = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
defineOptions({ name: "configDrawer" });
|
||||
|
||||
const drawer = ref(false);
|
||||
|
||||
function open() {
|
||||
drawer.value = true;
|
||||
}
|
||||
|
||||
function close() {
|
||||
drawer.value = false;
|
||||
}
|
||||
|
||||
defineExpose({ open, close });
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -13,56 +13,55 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { initDepartment } from "@/api/index";
|
||||
|
||||
export default {
|
||||
name: "departmentChoose",
|
||||
data() {
|
||||
return {
|
||||
selectDep: [],
|
||||
department: [],
|
||||
cascaderProps: {
|
||||
value: "value",
|
||||
label: "label",
|
||||
children: "children",
|
||||
checkStrictly: true,
|
||||
emitPath: true,
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
initDepartmentData() {
|
||||
initDepartment().then((res) => {
|
||||
if (res.success) {
|
||||
const arr = res.result;
|
||||
this.filterData(arr);
|
||||
this.department = arr;
|
||||
}
|
||||
});
|
||||
},
|
||||
handleChangeDep(value) {
|
||||
let departmentId = "";
|
||||
if (value && value.length > 0) {
|
||||
departmentId = value[value.length - 1];
|
||||
}
|
||||
this.$emit("on-change", departmentId);
|
||||
},
|
||||
clearSelect() {
|
||||
this.selectDep = [];
|
||||
},
|
||||
filterData(data) {
|
||||
data.forEach((e) => {
|
||||
e.value = e.id;
|
||||
e.label = e.title;
|
||||
if (e.children) {
|
||||
this.filterData(e.children);
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.initDepartmentData();
|
||||
},
|
||||
};
|
||||
defineOptions({ name: "departmentChoose" });
|
||||
|
||||
const emit = defineEmits(["on-change"]);
|
||||
|
||||
const selectDep = ref([]);
|
||||
const department = ref([]);
|
||||
const cascaderProps = ref({
|
||||
value: "value",
|
||||
label: "label",
|
||||
children: "children",
|
||||
checkStrictly: true,
|
||||
emitPath: true,
|
||||
});
|
||||
|
||||
function initDepartmentData() {
|
||||
initDepartment().then((res) => {
|
||||
if (res.success) {
|
||||
const arr = res.result;
|
||||
filterData(arr);
|
||||
department.value = arr;
|
||||
}
|
||||
});
|
||||
}
|
||||
function handleChangeDep(value) {
|
||||
let departmentId = "";
|
||||
if (value && value.length > 0) {
|
||||
departmentId = value[value.length - 1];
|
||||
}
|
||||
emit("on-change", departmentId);
|
||||
}
|
||||
function clearSelect() {
|
||||
selectDep.value = [];
|
||||
}
|
||||
function filterData(data) {
|
||||
data.forEach((e) => {
|
||||
e.value = e.id;
|
||||
e.label = e.title;
|
||||
if (e.children) {
|
||||
filterData(e.children);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initDepartmentData();
|
||||
|
||||
// userManage.vue 通过 depRef 引用本组件(当前未实际调用方法),作为公共组件 API 保留
|
||||
defineExpose({ clearSelect, selectDep });
|
||||
</script>
|
||||
|
||||
@@ -35,101 +35,100 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { initDepartment } from "@/api/index";
|
||||
|
||||
export default {
|
||||
name: "departmentTreeChoose",
|
||||
props: {
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "点击选择部门",
|
||||
},
|
||||
defineOptions({ name: "departmentTreeChoose" });
|
||||
|
||||
const props = defineProps({
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
depLoading: false,
|
||||
departmentTitle: "",
|
||||
searchKey: "",
|
||||
dataDep: [],
|
||||
cloneDep: [],
|
||||
departmentId: [],
|
||||
treeProps: {
|
||||
label: "title",
|
||||
children: "children",
|
||||
},
|
||||
};
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
methods: {
|
||||
initDepartmentData() {
|
||||
this.depLoading = true;
|
||||
initDepartment()
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
this.dataDep = res.result;
|
||||
this.cloneDep = JSON.parse(JSON.stringify(this.dataDep));
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.depLoading = false;
|
||||
});
|
||||
},
|
||||
searchDep() {
|
||||
if (this.searchKey) {
|
||||
this.dataDep = this.cloneDep.filter(
|
||||
(item) => item.title.indexOf(this.searchKey) > -1
|
||||
);
|
||||
} else {
|
||||
this.dataDep = JSON.parse(JSON.stringify(this.cloneDep));
|
||||
}
|
||||
},
|
||||
selectTree(node) {
|
||||
if (!node) {
|
||||
this.$emit("on-change", null);
|
||||
this.departmentId = "";
|
||||
this.departmentTitle = "";
|
||||
return;
|
||||
}
|
||||
this.departmentId = node.id;
|
||||
this.departmentTitle = node.title;
|
||||
this.$emit("on-change", {
|
||||
departmentId: this.departmentId,
|
||||
departmentTitle: this.departmentTitle,
|
||||
});
|
||||
},
|
||||
clearSelect() {
|
||||
this.departmentId = [];
|
||||
this.departmentTitle = "";
|
||||
this.initDepartmentData();
|
||||
if (this.multiple) {
|
||||
this.$emit("on-change", []);
|
||||
} else {
|
||||
this.$emit("on-change", "");
|
||||
}
|
||||
this.$emit("on-clear");
|
||||
},
|
||||
setData(ids, title) {
|
||||
this.departmentTitle = title;
|
||||
if (this.multiple) {
|
||||
this.departmentId = ids;
|
||||
} else {
|
||||
this.departmentId = [];
|
||||
this.departmentId.push(ids);
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.initDepartmentData();
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "点击选择部门",
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(["on-change", "on-clear"]);
|
||||
|
||||
const depLoading = ref(false);
|
||||
const departmentTitle = ref("");
|
||||
const searchKey = ref("");
|
||||
const dataDep = ref([]);
|
||||
const cloneDep = ref([]);
|
||||
const departmentId = ref([]);
|
||||
const treeProps = {
|
||||
label: "title",
|
||||
children: "children",
|
||||
};
|
||||
|
||||
function initDepartmentData() {
|
||||
depLoading.value = true;
|
||||
initDepartment()
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
dataDep.value = res.result;
|
||||
cloneDep.value = JSON.parse(JSON.stringify(dataDep.value));
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
depLoading.value = false;
|
||||
});
|
||||
}
|
||||
function searchDep() {
|
||||
if (searchKey.value) {
|
||||
dataDep.value = cloneDep.value.filter(
|
||||
(item) => item.title.indexOf(searchKey.value) > -1
|
||||
);
|
||||
} else {
|
||||
dataDep.value = JSON.parse(JSON.stringify(cloneDep.value));
|
||||
}
|
||||
}
|
||||
function selectTree(node) {
|
||||
if (!node) {
|
||||
emit("on-change", null);
|
||||
departmentId.value = "";
|
||||
departmentTitle.value = "";
|
||||
return;
|
||||
}
|
||||
departmentId.value = node.id;
|
||||
departmentTitle.value = node.title;
|
||||
emit("on-change", {
|
||||
departmentId: departmentId.value,
|
||||
departmentTitle: departmentTitle.value,
|
||||
});
|
||||
}
|
||||
function clearSelect() {
|
||||
departmentId.value = [];
|
||||
departmentTitle.value = "";
|
||||
initDepartmentData();
|
||||
if (props.multiple) {
|
||||
emit("on-change", []);
|
||||
} else {
|
||||
emit("on-change", "");
|
||||
}
|
||||
emit("on-clear");
|
||||
}
|
||||
function setData(ids, title) {
|
||||
departmentTitle.value = title;
|
||||
if (props.multiple) {
|
||||
departmentId.value = ids;
|
||||
} else {
|
||||
departmentId.value = [];
|
||||
departmentId.value.push(ids);
|
||||
}
|
||||
}
|
||||
|
||||
initDepartmentData();
|
||||
|
||||
// 父组件(userManage)通过 ref 调用 setData / clearSelect
|
||||
defineExpose({ setData, clearSelect });
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -29,99 +29,105 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "setPassword",
|
||||
props: {
|
||||
modelValue: String,
|
||||
value: String,
|
||||
size: String,
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "请输入密码,长度为6-20个字符",
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
maxlength: {
|
||||
type: Number,
|
||||
default: 20,
|
||||
},
|
||||
<script setup>
|
||||
import { ref, watch } from "vue";
|
||||
|
||||
defineOptions({ name: "setPassword" });
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: String,
|
||||
value: String,
|
||||
size: String,
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "请输入密码,长度为6-20个字符",
|
||||
},
|
||||
emits: ["update:modelValue", "input", "on-change"],
|
||||
data() {
|
||||
return {
|
||||
currentValue: this.modelValue ?? this.value ?? "",
|
||||
tipStyle: "password-tip-none",
|
||||
strengthValue: 0,
|
||||
progressStatus: "",
|
||||
strength: "无",
|
||||
grade: 0,
|
||||
};
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
watch: {
|
||||
modelValue(val) {
|
||||
this.setCurrentValue(val);
|
||||
},
|
||||
value(val) {
|
||||
this.setCurrentValue(val);
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
methods: {
|
||||
checkStrengthValue(v) {
|
||||
let grade = 0;
|
||||
if (/\d/.test(v)) grade++;
|
||||
if (/[a-z]/.test(v)) grade++;
|
||||
if (/[A-Z]/.test(v)) grade++;
|
||||
if (/\W/.test(v)) grade++;
|
||||
if (v.length >= 10) grade++;
|
||||
this.grade = grade;
|
||||
return grade;
|
||||
},
|
||||
strengthChange() {
|
||||
if (!this.currentValue) {
|
||||
this.tipStyle = "password-tip-none";
|
||||
this.strength = "无";
|
||||
this.strengthValue = 0;
|
||||
return;
|
||||
}
|
||||
const grade = this.checkStrengthValue(this.currentValue);
|
||||
if (grade <= 1) {
|
||||
this.progressStatus = "exception";
|
||||
this.tipStyle = "password-tip-weak";
|
||||
this.strength = "弱";
|
||||
this.strengthValue = 33;
|
||||
} else if (grade >= 2 && grade <= 4) {
|
||||
this.progressStatus = "";
|
||||
this.tipStyle = "password-tip-middle";
|
||||
this.strength = "中";
|
||||
this.strengthValue = 66;
|
||||
} else {
|
||||
this.progressStatus = "success";
|
||||
this.tipStyle = "password-tip-strong";
|
||||
this.strength = "强";
|
||||
this.strengthValue = 100;
|
||||
}
|
||||
},
|
||||
handleChange() {
|
||||
this.strengthChange();
|
||||
this.$emit("update:modelValue", this.currentValue);
|
||||
this.$emit("input", this.currentValue);
|
||||
this.$emit("on-change", this.currentValue, this.grade, this.strength);
|
||||
},
|
||||
setCurrentValue(value) {
|
||||
if (value === this.currentValue) return;
|
||||
this.currentValue = value ?? "";
|
||||
this.strengthChange();
|
||||
this.$emit("on-change", this.currentValue, this.grade, this.strength);
|
||||
},
|
||||
maxlength: {
|
||||
type: Number,
|
||||
default: 20,
|
||||
},
|
||||
};
|
||||
});
|
||||
const emit = defineEmits(["update:modelValue", "input", "on-change"]);
|
||||
|
||||
const currentValue = ref(props.modelValue ?? props.value ?? "");
|
||||
const tipStyle = ref("password-tip-none");
|
||||
const strengthValue = ref(0);
|
||||
const progressStatus = ref("");
|
||||
const strength = ref("无");
|
||||
const grade = ref(0);
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
setCurrentValue(val);
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => props.value,
|
||||
(val) => {
|
||||
setCurrentValue(val);
|
||||
}
|
||||
);
|
||||
|
||||
function checkStrengthValue(v) {
|
||||
// 局部变量原名 grade 与顶层 ref 同名,改名避免遮蔽
|
||||
let g = 0;
|
||||
if (/\d/.test(v)) g++;
|
||||
if (/[a-z]/.test(v)) g++;
|
||||
if (/[A-Z]/.test(v)) g++;
|
||||
if (/\W/.test(v)) g++;
|
||||
if (v.length >= 10) g++;
|
||||
grade.value = g;
|
||||
return g;
|
||||
}
|
||||
|
||||
function strengthChange() {
|
||||
if (!currentValue.value) {
|
||||
tipStyle.value = "password-tip-none";
|
||||
strength.value = "无";
|
||||
strengthValue.value = 0;
|
||||
return;
|
||||
}
|
||||
const grade = checkStrengthValue(currentValue.value);
|
||||
if (grade <= 1) {
|
||||
progressStatus.value = "exception";
|
||||
tipStyle.value = "password-tip-weak";
|
||||
strength.value = "弱";
|
||||
strengthValue.value = 33;
|
||||
} else if (grade >= 2 && grade <= 4) {
|
||||
progressStatus.value = "";
|
||||
tipStyle.value = "password-tip-middle";
|
||||
strength.value = "中";
|
||||
strengthValue.value = 66;
|
||||
} else {
|
||||
progressStatus.value = "success";
|
||||
tipStyle.value = "password-tip-strong";
|
||||
strength.value = "强";
|
||||
strengthValue.value = 100;
|
||||
}
|
||||
}
|
||||
|
||||
function handleChange() {
|
||||
strengthChange();
|
||||
emit("update:modelValue", currentValue.value);
|
||||
emit("input", currentValue.value);
|
||||
emit("on-change", currentValue.value, grade.value, strength.value);
|
||||
}
|
||||
|
||||
function setCurrentValue(value) {
|
||||
if (value === currentValue.value) return;
|
||||
currentValue.value = value ?? "";
|
||||
strengthChange();
|
||||
emit("on-change", currentValue.value, grade.value, strength.value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
@closed="resetPicModal"
|
||||
>
|
||||
<ossManage
|
||||
ref="ossManage"
|
||||
ref="ossManageRef"
|
||||
:is-component="true"
|
||||
:initialize="picModalFlag"
|
||||
:max-select="1"
|
||||
@@ -70,110 +70,112 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { nextTick, ref, useAttrs, watch } from "vue";
|
||||
import { View } from "@element-plus/icons-vue";
|
||||
import { uploadFile } from "@/api/index";
|
||||
import { getStore } from "@/libs/storage";
|
||||
import ossManage from "@/views/sys/oss-manage/ossManage";
|
||||
|
||||
export default {
|
||||
name: "uploadPicInput",
|
||||
components: {
|
||||
ossManage,
|
||||
View,
|
||||
defineOptions({ name: "uploadPicInput" });
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: String,
|
||||
value: String,
|
||||
size: {
|
||||
default: "default",
|
||||
type: String,
|
||||
},
|
||||
props: {
|
||||
modelValue: String,
|
||||
value: String,
|
||||
size: {
|
||||
default: "default",
|
||||
type: String,
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "图片链接",
|
||||
},
|
||||
showInput: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
maxlength: Number,
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "图片链接",
|
||||
},
|
||||
emits: ["update:modelValue", "input", "on-change"],
|
||||
data() {
|
||||
return {
|
||||
accessToken: {},
|
||||
currentValue: this.modelValue ?? this.value ?? "",
|
||||
viewImage: false,
|
||||
uploadFileUrl: uploadFile,
|
||||
picModalFlag: false,
|
||||
selectedFormBtnName: "",
|
||||
picIndex: "",
|
||||
};
|
||||
showInput: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
methods: {
|
||||
handleCLickImg(val, index) {
|
||||
this.picModalFlag = true;
|
||||
this.selectedFormBtnName = val;
|
||||
this.picIndex = index;
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.ossManage) {
|
||||
this.$refs.ossManage.selectImage = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
handleOssCallback(item) {
|
||||
this.picModalFlag = false;
|
||||
if (!item?.url) return;
|
||||
this.currentValue = item.url;
|
||||
this.picIndex = "";
|
||||
this.emitValue(this.currentValue);
|
||||
},
|
||||
resetPicModal() {
|
||||
this.selectedFormBtnName = "";
|
||||
this.picIndex = "";
|
||||
},
|
||||
init() {
|
||||
this.accessToken = {
|
||||
accessToken: this.getStore("accessToken"),
|
||||
};
|
||||
},
|
||||
emitValue(val) {
|
||||
this.$emit("update:modelValue", val);
|
||||
this.$emit("input", val);
|
||||
this.$emit("on-change", val);
|
||||
},
|
||||
handleChange() {
|
||||
this.emitValue(this.currentValue);
|
||||
this.$attrs.rollback && this.$attrs.rollback();
|
||||
},
|
||||
setCurrentValue(value) {
|
||||
if (value === this.currentValue) {
|
||||
return;
|
||||
}
|
||||
this.currentValue = value ?? "";
|
||||
this.emitValue(this.currentValue);
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
watch: {
|
||||
modelValue(val) {
|
||||
this.setCurrentValue(val);
|
||||
},
|
||||
value(val) {
|
||||
this.setCurrentValue(val);
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
created() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
maxlength: Number,
|
||||
});
|
||||
const emit = defineEmits(["update:modelValue", "input", "on-change"]);
|
||||
const attrs = useAttrs();
|
||||
|
||||
const accessToken = ref({});
|
||||
const currentValue = ref(props.modelValue ?? props.value ?? "");
|
||||
const viewImage = ref(false);
|
||||
const uploadFileUrl = ref(uploadFile);
|
||||
const picModalFlag = ref(false);
|
||||
const selectedFormBtnName = ref("");
|
||||
const picIndex = ref("");
|
||||
|
||||
// 模板 ref:ossManage 与 import 组件同名,改名避免互相覆盖
|
||||
const ossManageRef = ref(null);
|
||||
|
||||
function handleCLickImg(val, index) {
|
||||
picModalFlag.value = true;
|
||||
selectedFormBtnName.value = val;
|
||||
picIndex.value = index;
|
||||
nextTick(() => {
|
||||
if (ossManageRef.value) {
|
||||
ossManageRef.value.selectImage = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
function handleOssCallback(item) {
|
||||
picModalFlag.value = false;
|
||||
if (!item?.url) return;
|
||||
currentValue.value = item.url;
|
||||
picIndex.value = "";
|
||||
emitValue(currentValue.value);
|
||||
}
|
||||
function resetPicModal() {
|
||||
selectedFormBtnName.value = "";
|
||||
picIndex.value = "";
|
||||
}
|
||||
function init() {
|
||||
accessToken.value = {
|
||||
accessToken: getStore("accessToken"),
|
||||
};
|
||||
}
|
||||
function emitValue(val) {
|
||||
emit("update:modelValue", val);
|
||||
emit("input", val);
|
||||
emit("on-change", val);
|
||||
}
|
||||
function handleChange() {
|
||||
emitValue(currentValue.value);
|
||||
attrs.rollback && attrs.rollback();
|
||||
}
|
||||
function setCurrentValue(value) {
|
||||
if (value === currentValue.value) {
|
||||
return;
|
||||
}
|
||||
currentValue.value = value ?? "";
|
||||
emitValue(currentValue.value);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
setCurrentValue(val);
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => props.value,
|
||||
(val) => {
|
||||
setCurrentValue(val);
|
||||
}
|
||||
);
|
||||
|
||||
// 原 created 钩子
|
||||
init();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -75,167 +75,167 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { computed, onMounted, ref, watch } from "vue";
|
||||
import { Camera, Delete, View } from "@element-plus/icons-vue";
|
||||
import { uploadFile } from "@/api/index";
|
||||
import { getStore } from "@/libs/storage";
|
||||
import { Message } from "@/utils/message";
|
||||
import vuedraggable from "vuedraggable";
|
||||
|
||||
export default {
|
||||
name: "uploadPicThumb",
|
||||
components: {
|
||||
vuedraggable,
|
||||
Camera,
|
||||
Delete,
|
||||
View,
|
||||
defineOptions({ name: "uploadPicThumb" });
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: null },
|
||||
value: { type: null },
|
||||
draggable: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
props: {
|
||||
modelValue: { type: null },
|
||||
value: { type: null },
|
||||
draggable: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 10,
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
emits: ["update:modelValue", "input", "on-change", "uploadchange"],
|
||||
data() {
|
||||
return {
|
||||
accessToken: {},
|
||||
uploadFileUrl: uploadFile,
|
||||
uploadList: [],
|
||||
viewImage: false,
|
||||
imgUrl: "",
|
||||
};
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 10,
|
||||
},
|
||||
computed: {
|
||||
bindValue() {
|
||||
return this.modelValue !== undefined ? this.modelValue : this.value;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
draggableItemKey(item) {
|
||||
return item.url || item.uid || item.name;
|
||||
},
|
||||
onEnd() {
|
||||
this.returnValue();
|
||||
},
|
||||
init() {
|
||||
this.setData(this.bindValue, true);
|
||||
this.accessToken = {
|
||||
accessToken: this.getStore("accessToken"),
|
||||
};
|
||||
},
|
||||
handleView(imgUrl) {
|
||||
this.imgUrl = imgUrl;
|
||||
this.viewImage = true;
|
||||
},
|
||||
handleRemove(file) {
|
||||
this.uploadList = this.uploadList.filter((i) => i.url !== file.url);
|
||||
this.returnValue();
|
||||
},
|
||||
handleSuccess(res, file) {
|
||||
if (res.success) {
|
||||
file.url = res.result;
|
||||
if (!this.multiple && this.uploadList.length > 0) {
|
||||
this.uploadList.splice(0, 1);
|
||||
}
|
||||
this.uploadList.push(file);
|
||||
this.returnValue();
|
||||
} else {
|
||||
this.$Message.error(res.message);
|
||||
}
|
||||
},
|
||||
handleError(error) {
|
||||
this.$Message.error(error.toString());
|
||||
},
|
||||
handleBeforeUpload(file) {
|
||||
const okType = ["image/jpeg", "image/png", "image/gif", "image/jpg"].includes(file.type);
|
||||
if (!okType) {
|
||||
this.$Message.warning("请选择 .jpg .jpeg .png .gif 格式图片");
|
||||
return false;
|
||||
}
|
||||
if (file.size / 1024 > 1024) {
|
||||
this.$Message.warning("所选文件大小过大,不能超过 1M");
|
||||
return false;
|
||||
}
|
||||
if (this.multiple && this.uploadList.length >= this.limit) {
|
||||
this.$Message.warning("最多只能上传" + this.limit + "张图片");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
emitValue(val) {
|
||||
this.$emit("update:modelValue", val);
|
||||
this.$emit("input", val);
|
||||
this.$emit("on-change", val);
|
||||
},
|
||||
returnValue() {
|
||||
if (!this.uploadList || this.uploadList.length < 1) {
|
||||
const empty = this.multiple ? [] : "";
|
||||
this.emitValue(empty);
|
||||
return;
|
||||
}
|
||||
if (!this.multiple) {
|
||||
this.emitValue(this.uploadList[0].url);
|
||||
} else {
|
||||
this.emitValue(this.uploadList.map((e) => e.url));
|
||||
}
|
||||
},
|
||||
setData(v, init) {
|
||||
if (typeof v == "string") {
|
||||
if (this.multiple) {
|
||||
this.$Message.warning("多张上传仅支持数组数据类型");
|
||||
return;
|
||||
}
|
||||
if (!v) {
|
||||
return;
|
||||
}
|
||||
this.uploadList = [{ url: v, status: "finished" }];
|
||||
this.$emit("uploadchange", v);
|
||||
this.emitValue(v);
|
||||
} else if (typeof v == "object" && v) {
|
||||
if (!this.multiple) {
|
||||
this.$Message.warning("单张上传仅支持字符串数据类型");
|
||||
return;
|
||||
}
|
||||
this.uploadList = [];
|
||||
const list = v.length > this.limit ? v.slice(0, this.limit) : v;
|
||||
if (v.length > this.limit) {
|
||||
this.$Message.warning("最多只能上传" + this.limit + "张图片");
|
||||
}
|
||||
list.forEach((e) => {
|
||||
this.uploadList.push({
|
||||
status: "finished",
|
||||
...(typeof e === "string" ? { url: e } : e),
|
||||
});
|
||||
});
|
||||
if (init) {
|
||||
this.emitValue(list);
|
||||
} else {
|
||||
this.$emit("on-change", list);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
modelValue(val) {
|
||||
this.setData(val);
|
||||
},
|
||||
value(val) {
|
||||
this.setData(val);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
});
|
||||
const emit = defineEmits(["update:modelValue", "input", "on-change", "uploadchange"]);
|
||||
|
||||
const accessToken = ref({});
|
||||
const uploadFileUrl = uploadFile;
|
||||
const uploadList = ref([]);
|
||||
const viewImage = ref(false);
|
||||
const imgUrl = ref("");
|
||||
|
||||
// el-upload 模板 ref
|
||||
const upload = ref(null);
|
||||
|
||||
const bindValue = computed(() => {
|
||||
return props.modelValue !== undefined ? props.modelValue : props.value;
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
setData(val);
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => props.value,
|
||||
(val) => {
|
||||
setData(val);
|
||||
}
|
||||
);
|
||||
|
||||
function draggableItemKey(item) {
|
||||
return item.url || item.uid || item.name;
|
||||
}
|
||||
function onEnd() {
|
||||
returnValue();
|
||||
}
|
||||
function init() {
|
||||
setData(bindValue.value, true);
|
||||
accessToken.value = {
|
||||
accessToken: getStore("accessToken"),
|
||||
};
|
||||
}
|
||||
function handleView(url) {
|
||||
imgUrl.value = url;
|
||||
viewImage.value = true;
|
||||
}
|
||||
function handleRemove(file) {
|
||||
uploadList.value = uploadList.value.filter((i) => i.url !== file.url);
|
||||
returnValue();
|
||||
}
|
||||
function handleSuccess(res, file) {
|
||||
if (res.success) {
|
||||
file.url = res.result;
|
||||
if (!props.multiple && uploadList.value.length > 0) {
|
||||
uploadList.value.splice(0, 1);
|
||||
}
|
||||
uploadList.value.push(file);
|
||||
returnValue();
|
||||
} else {
|
||||
Message.error(res.message);
|
||||
}
|
||||
}
|
||||
function handleError(error) {
|
||||
Message.error(error.toString());
|
||||
}
|
||||
function handleBeforeUpload(file) {
|
||||
const okType = ["image/jpeg", "image/png", "image/gif", "image/jpg"].includes(file.type);
|
||||
if (!okType) {
|
||||
Message.warning("请选择 .jpg .jpeg .png .gif 格式图片");
|
||||
return false;
|
||||
}
|
||||
if (file.size / 1024 > 1024) {
|
||||
Message.warning("所选文件大小过大,不能超过 1M");
|
||||
return false;
|
||||
}
|
||||
if (props.multiple && uploadList.value.length >= props.limit) {
|
||||
Message.warning("最多只能上传" + props.limit + "张图片");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function emitValue(val) {
|
||||
emit("update:modelValue", val);
|
||||
emit("input", val);
|
||||
emit("on-change", val);
|
||||
}
|
||||
function returnValue() {
|
||||
if (!uploadList.value || uploadList.value.length < 1) {
|
||||
const empty = props.multiple ? [] : "";
|
||||
emitValue(empty);
|
||||
return;
|
||||
}
|
||||
if (!props.multiple) {
|
||||
emitValue(uploadList.value[0].url);
|
||||
} else {
|
||||
emitValue(uploadList.value.map((e) => e.url));
|
||||
}
|
||||
}
|
||||
function setData(v, init) {
|
||||
if (typeof v == "string") {
|
||||
if (props.multiple) {
|
||||
Message.warning("多张上传仅支持数组数据类型");
|
||||
return;
|
||||
}
|
||||
if (!v) {
|
||||
return;
|
||||
}
|
||||
uploadList.value = [{ url: v, status: "finished" }];
|
||||
emit("uploadchange", v);
|
||||
emitValue(v);
|
||||
} else if (typeof v == "object" && v) {
|
||||
if (!props.multiple) {
|
||||
Message.warning("单张上传仅支持字符串数据类型");
|
||||
return;
|
||||
}
|
||||
uploadList.value = [];
|
||||
const list = v.length > props.limit ? v.slice(0, props.limit) : v;
|
||||
if (v.length > props.limit) {
|
||||
Message.warning("最多只能上传" + props.limit + "张图片");
|
||||
}
|
||||
list.forEach((e) => {
|
||||
uploadList.value.push({
|
||||
status: "finished",
|
||||
...(typeof e === "string" ? { url: e } : e),
|
||||
});
|
||||
});
|
||||
if (init) {
|
||||
emitValue(list);
|
||||
} else {
|
||||
emit("on-change", list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -19,132 +19,129 @@
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, ref, watch } from "vue";
|
||||
import AMapLoader from "@amap/amap-jsapi-loader";
|
||||
import { getRegion } from "@/api/common.js";
|
||||
import config from "@/config/index";
|
||||
import { Message } from "@/utils/message";
|
||||
|
||||
export default {
|
||||
name: "map",
|
||||
data() {
|
||||
return {
|
||||
config,
|
||||
showMap: false, // 地图显隐
|
||||
mapSearch: "", // 地图搜索
|
||||
map: null, // 初始化地图
|
||||
autoComplete: null, // 初始化搜索方法
|
||||
geocoder: null, // 初始化地理、坐标转化
|
||||
positionPicker: null, // 地图拖拽选点
|
||||
tips: [], //搜索关键字列表
|
||||
addrContent: {}, // 回显地址信息
|
||||
loading: false, // 加载状态
|
||||
defineOptions({ name: "map" });
|
||||
|
||||
const emit = defineEmits(["getAddress"]);
|
||||
|
||||
const showMap = ref(false); // 地图显隐
|
||||
const mapSearch = ref(""); // 地图搜索
|
||||
const map = ref(null); // 初始化地图
|
||||
const autoComplete = ref(null); // 初始化搜索方法
|
||||
const geocoder = ref(null); // 初始化地理、坐标转化
|
||||
const positionPicker = ref(null); // 地图拖拽选点
|
||||
const tips = ref([]); //搜索关键字列表
|
||||
const addrContent = ref({}); // 回显地址信息
|
||||
const loading = ref(false); // 加载状态
|
||||
|
||||
watch(mapSearch, (val) => {
|
||||
searchOfMap(val);
|
||||
});
|
||||
|
||||
function ok() {
|
||||
|
||||
if (addrContent.value && addrContent.value.regeocode) {
|
||||
const params = {
|
||||
cityCode: addrContent.value.regeocode.addressComponent.citycode,
|
||||
townName: addrContent.value.regeocode.addressComponent.township,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
mapSearch: function (val) {
|
||||
this.searchOfMap(val);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
ok() {
|
||||
getRegion(params).then((res) => {
|
||||
if (res.success) {
|
||||
addrContent.value.addr = res.result.name.replace(/,/g, " ");
|
||||
addrContent.value.addrId = res.result.id;
|
||||
loading.value = false;
|
||||
|
||||
if (this.addrContent && this.addrContent.regeocode) {
|
||||
const params = {
|
||||
cityCode: this.addrContent.regeocode.addressComponent.citycode,
|
||||
townName: this.addrContent.regeocode.addressComponent.township,
|
||||
};
|
||||
getRegion(params).then((res) => {
|
||||
if (res.success) {
|
||||
this.addrContent.addr = res.result.name.replace(/,/g, " ");
|
||||
this.addrContent.addrId = res.result.id;
|
||||
this.loading = false;
|
||||
|
||||
this.$emit("getAddress", this.addrContent);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.$Message.error('未获取到坐标信息!请查看高德API配置是否正确')
|
||||
emit("getAddress", addrContent.value);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Message.error('未获取到坐标信息!请查看高德API配置是否正确')
|
||||
}
|
||||
|
||||
}
|
||||
function init() {
|
||||
AMapLoader.load({
|
||||
key: config.aMapKey, // 申请好的Web端开发者Key,首次调用 load 时必填
|
||||
version: "", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
|
||||
plugins: [
|
||||
"AMap.ToolBar",
|
||||
"AMap.Autocomplete",
|
||||
"AMap.PlaceSearch",
|
||||
"AMap.Geolocation",
|
||||
"AMap.Geocoder",
|
||||
], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
|
||||
AMapUI: {
|
||||
// 是否加载 AMapUI,缺省不加载
|
||||
version: "1.1", // AMapUI 缺省 1.1
|
||||
plugins: ["misc/PositionPicker"], // 需要加载的 AMapUI ui插件
|
||||
},
|
||||
init() {
|
||||
AMapLoader.load({
|
||||
key: this.config.aMapKey, // 申请好的Web端开发者Key,首次调用 load 时必填
|
||||
version: "", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
|
||||
plugins: [
|
||||
"AMap.ToolBar",
|
||||
"AMap.Autocomplete",
|
||||
"AMap.PlaceSearch",
|
||||
"AMap.Geolocation",
|
||||
"AMap.Geocoder",
|
||||
], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
|
||||
AMapUI: {
|
||||
// 是否加载 AMapUI,缺省不加载
|
||||
version: "1.1", // AMapUI 缺省 1.1
|
||||
plugins: ["misc/PositionPicker"], // 需要加载的 AMapUI ui插件
|
||||
},
|
||||
})
|
||||
.then((AMap) => {
|
||||
let that = this;
|
||||
this.map = new AMap.Map("map-container", {
|
||||
zoom: 12,
|
||||
});
|
||||
that.map.addControl(new AMap.ToolBar());
|
||||
that.map.addControl(new AMap.Autocomplete());
|
||||
that.map.addControl(new AMap.PlaceSearch());
|
||||
that.map.addControl(new AMap.Geocoder());
|
||||
|
||||
// 实例化Autocomplete
|
||||
let autoOptions = {
|
||||
city: "全国",
|
||||
};
|
||||
that.autoComplete = new AMap.Autocomplete(autoOptions); // 搜索
|
||||
that.geocoder = new AMap.Geocoder(autoOptions);
|
||||
|
||||
that.positionPicker = new AMapUI.PositionPicker({
|
||||
// 拖拽选点
|
||||
mode: "dragMap",
|
||||
map: that.map,
|
||||
});
|
||||
that.positionPicker.start();
|
||||
/**
|
||||
*
|
||||
* 所有回显数据,都在positionResult里面
|
||||
* 需要字段可以查找
|
||||
*
|
||||
*/
|
||||
that.positionPicker.on("success", function (positionResult) {
|
||||
that.addrContent = positionResult;
|
||||
});
|
||||
})
|
||||
.catch((e) => { });
|
||||
},
|
||||
searchOfMap(val) {
|
||||
// 地图搜索
|
||||
let that = this;
|
||||
this.autoComplete.search(val, function (status, result) {
|
||||
// 搜索成功时,result即是对应的匹配数据
|
||||
if (status == "complete" && result.info == "OK") {
|
||||
that.tips = result.tips;
|
||||
} else {
|
||||
that.tips = [];
|
||||
}
|
||||
})
|
||||
.then((AMap) => {
|
||||
map.value = new AMap.Map("map-container", {
|
||||
zoom: 12,
|
||||
});
|
||||
},
|
||||
selectAddr(location) {
|
||||
// 选择坐标
|
||||
if (!location) {
|
||||
this.$Message.warning("请选择正确点位");
|
||||
return false;
|
||||
}
|
||||
const lnglat = [location.lng, location.lat];
|
||||
this.positionPicker.start(lnglat);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
map.value.addControl(new AMap.ToolBar());
|
||||
map.value.addControl(new AMap.Autocomplete());
|
||||
map.value.addControl(new AMap.PlaceSearch());
|
||||
map.value.addControl(new AMap.Geocoder());
|
||||
|
||||
// 实例化Autocomplete
|
||||
let autoOptions = {
|
||||
city: "全国",
|
||||
};
|
||||
autoComplete.value = new AMap.Autocomplete(autoOptions); // 搜索
|
||||
geocoder.value = new AMap.Geocoder(autoOptions);
|
||||
|
||||
positionPicker.value = new AMapUI.PositionPicker({
|
||||
// 拖拽选点
|
||||
mode: "dragMap",
|
||||
map: map.value,
|
||||
});
|
||||
positionPicker.value.start();
|
||||
/**
|
||||
*
|
||||
* 所有回显数据,都在positionResult里面
|
||||
* 需要字段可以查找
|
||||
*
|
||||
*/
|
||||
positionPicker.value.on("success", function (positionResult) {
|
||||
addrContent.value = positionResult;
|
||||
});
|
||||
})
|
||||
.catch((e) => { });
|
||||
}
|
||||
function searchOfMap(val) {
|
||||
// 地图搜索
|
||||
autoComplete.value.search(val, function (status, result) {
|
||||
// 搜索成功时,result即是对应的匹配数据
|
||||
if (status == "complete" && result.info == "OK") {
|
||||
tips.value = result.tips;
|
||||
} else {
|
||||
tips.value = [];
|
||||
}
|
||||
});
|
||||
}
|
||||
function selectAddr(location) {
|
||||
// 选择坐标
|
||||
if (!location) {
|
||||
Message.warning("请选择正确点位");
|
||||
return false;
|
||||
}
|
||||
const lnglat = [location.lng, location.lat];
|
||||
positionPicker.value.start(lnglat);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
|
||||
// 唯一引用方 multiple-map.vue 通过 ref 引用本组件但从不访问任何成员(仅监听 getAddress 事件),无需暴露成员
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
#map-container {
|
||||
|
||||
@@ -40,85 +40,87 @@
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { aMapSwitch } from "@/config/index";
|
||||
import mapping from "@/components/map/index.vue";
|
||||
import * as API_Setup from "@/api/common.js";
|
||||
import { Message } from "@/utils/message";
|
||||
|
||||
export default {
|
||||
components: { mapping },
|
||||
data() {
|
||||
return {
|
||||
aMapSwitch,
|
||||
enableMap: false,
|
||||
mapDefault: "select",
|
||||
data: {
|
||||
province: [],
|
||||
city: [],
|
||||
area: [],
|
||||
street: [],
|
||||
},
|
||||
chiosend: [],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.chiosend = new Array(4).fill("");
|
||||
},
|
||||
methods: {
|
||||
open() {
|
||||
this.enableMap = true;
|
||||
this.init({ id: 0 }, "province");
|
||||
},
|
||||
changeMap(val) {
|
||||
this.mapDefault = val;
|
||||
},
|
||||
init(val, level = "province", index) {
|
||||
if (level == 0) {
|
||||
this.chiosend.splice(3, 1, val);
|
||||
} else {
|
||||
API_Setup.getChildRegion(val.id).then((res) => {
|
||||
if (res.result.length && val.id !== 0) {
|
||||
this.chiosend[index] = val;
|
||||
} else if (!res.result.length) {
|
||||
this.chiosend[index] = val;
|
||||
}
|
||||
this.data[level] = res.result;
|
||||
if (level == "city") {
|
||||
this.data.area = [];
|
||||
this.data.street = [];
|
||||
this.chiosend.splice(1, 3, "", "", "");
|
||||
}
|
||||
if (level == "area") {
|
||||
this.data.street = [];
|
||||
this.chiosend.splice(2, 2, "", "");
|
||||
}
|
||||
if (level == "street") {
|
||||
this.chiosend.splice(3, 1, "");
|
||||
}
|
||||
});
|
||||
const emit = defineEmits(["callback"]);
|
||||
|
||||
const enableMap = ref(false);
|
||||
const mapDefault = ref("select");
|
||||
const data = reactive({
|
||||
province: [],
|
||||
city: [],
|
||||
area: [],
|
||||
street: [],
|
||||
});
|
||||
const chiosend = ref([]);
|
||||
|
||||
// 模板 ref,对应高德地图子组件
|
||||
const map = ref(null);
|
||||
|
||||
onMounted(() => {
|
||||
chiosend.value = new Array(4).fill("");
|
||||
});
|
||||
|
||||
function open() {
|
||||
enableMap.value = true;
|
||||
init({ id: 0 }, "province");
|
||||
}
|
||||
function changeMap(val) {
|
||||
mapDefault.value = val;
|
||||
}
|
||||
function init(val, level = "province", index) {
|
||||
if (level == 0) {
|
||||
chiosend.value.splice(3, 1, val);
|
||||
} else {
|
||||
API_Setup.getChildRegion(val.id).then((res) => {
|
||||
if (res.result.length && val.id !== 0) {
|
||||
chiosend.value[index] = val;
|
||||
} else if (!res.result.length) {
|
||||
chiosend.value[index] = val;
|
||||
}
|
||||
},
|
||||
getAddress(center) {
|
||||
this.$emit("callback", {
|
||||
type: this.mapDefault,
|
||||
data: center,
|
||||
});
|
||||
this.enableMap = false;
|
||||
},
|
||||
finished() {
|
||||
if (!this.chiosend[0]) {
|
||||
this.$Message.error("请选择地址");
|
||||
return;
|
||||
data[level] = res.result;
|
||||
if (level == "city") {
|
||||
data.area = [];
|
||||
data.street = [];
|
||||
chiosend.value.splice(1, 3, "", "", "");
|
||||
}
|
||||
const params = this.chiosend.filter((item) => item !== "" && item.value !== "");
|
||||
this.enableMap = false;
|
||||
this.$emit("callback", {
|
||||
type: this.mapDefault,
|
||||
data: params,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
if (level == "area") {
|
||||
data.street = [];
|
||||
chiosend.value.splice(2, 2, "", "");
|
||||
}
|
||||
if (level == "street") {
|
||||
chiosend.value.splice(3, 1, "");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
function getAddress(center) {
|
||||
emit("callback", {
|
||||
type: mapDefault.value,
|
||||
data: center,
|
||||
});
|
||||
enableMap.value = false;
|
||||
}
|
||||
function finished() {
|
||||
if (!chiosend.value[0]) {
|
||||
Message.error("请选择地址");
|
||||
return;
|
||||
}
|
||||
const params = chiosend.value.filter((item) => item !== "" && item.value !== "");
|
||||
enableMap.value = false;
|
||||
emit("callback", {
|
||||
type: mapDefault.value,
|
||||
data: params,
|
||||
});
|
||||
}
|
||||
|
||||
// 父组件(member/index、memberRecycle、memberDetail、orderDetail、shopOperation)通过 ref 调用 open() 打开弹窗
|
||||
defineExpose({ open });
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -5,54 +5,56 @@
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { computed, getCurrentInstance } from "vue";
|
||||
import { unitPrice } from "@/utils/filters";
|
||||
|
||||
export default {
|
||||
name: "priceColorScheme",
|
||||
props: {
|
||||
value: {
|
||||
default: 0,
|
||||
validator(val) {
|
||||
return (
|
||||
val === null ||
|
||||
val === undefined ||
|
||||
typeof val === "number" ||
|
||||
typeof val === "string"
|
||||
);
|
||||
},
|
||||
},
|
||||
unit: {
|
||||
type: String,
|
||||
default: "¥",
|
||||
},
|
||||
dot: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
customStyle: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
defineOptions({ name: "priceColorScheme" });
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
default: 0,
|
||||
validator(val) {
|
||||
return (
|
||||
val === null ||
|
||||
val === undefined ||
|
||||
typeof val === "number" ||
|
||||
typeof val === "string"
|
||||
);
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
displayText() {
|
||||
const val = this.value;
|
||||
if (val === null || val === undefined || val === "" || val === "null") {
|
||||
return `${this.unit || "¥"}0.00`;
|
||||
}
|
||||
return unitPrice(val, this.unit);
|
||||
},
|
||||
priceStyle() {
|
||||
const resolvedColor = this.color || this.$mainColor || "";
|
||||
return resolvedColor
|
||||
? { color: resolvedColor, ...this.customStyle }
|
||||
: { ...this.customStyle };
|
||||
},
|
||||
unit: {
|
||||
type: String,
|
||||
default: "¥",
|
||||
},
|
||||
};
|
||||
dot: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
customStyle: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
|
||||
// $mainColor 为 main.js 注册的全局属性
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const displayText = computed(() => {
|
||||
const val = props.value;
|
||||
if (val === null || val === undefined || val === "" || val === "null") {
|
||||
return `${props.unit || "¥"}0.00`;
|
||||
}
|
||||
return unitPrice(val, props.unit);
|
||||
});
|
||||
const priceStyle = computed(() => {
|
||||
const resolvedColor = props.color || proxy.$mainColor || "";
|
||||
return resolvedColor
|
||||
? { color: resolvedColor, ...props.customStyle }
|
||||
: { ...props.customStyle };
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -10,140 +10,140 @@
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
<script setup>
|
||||
import { computed, ref, watch } from "vue";
|
||||
import { getChildRegion } from "@/api/common.js";
|
||||
|
||||
export default {
|
||||
props: ["addressId"],
|
||||
data() {
|
||||
return {
|
||||
data: [],
|
||||
addr: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
cascaderProps() {
|
||||
return {
|
||||
value: "value",
|
||||
label: "label",
|
||||
lazy: true,
|
||||
lazyLoad: this.loadData,
|
||||
};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
change(val) {
|
||||
if (!val || !val.length) {
|
||||
this.$emit("selected", [[], []]);
|
||||
return;
|
||||
}
|
||||
const labels = this.resolveLabels(val);
|
||||
this.$emit("selected", [val, labels]);
|
||||
},
|
||||
resolveLabels(values) {
|
||||
const labels = [];
|
||||
let options = this.data;
|
||||
for (const v of values) {
|
||||
const node = options.find((item) => item.value === v);
|
||||
if (!node) break;
|
||||
labels.push(node.label);
|
||||
options = node.children || [];
|
||||
}
|
||||
return labels;
|
||||
},
|
||||
loadData(node, resolve) {
|
||||
const parentId = node.level === 0 ? 0 : node.value;
|
||||
getChildRegion(parentId).then((res) => {
|
||||
if (!res.result || res.result.length <= 0) {
|
||||
resolve([]);
|
||||
return;
|
||||
}
|
||||
const nodes = res.result.map((child) => {
|
||||
const isLeaf =
|
||||
child.level === "street" ||
|
||||
node.label === "香港特别行政区" ||
|
||||
child.name === "台湾省";
|
||||
return {
|
||||
value: child.id,
|
||||
label: child.name,
|
||||
leaf: isLeaf,
|
||||
};
|
||||
});
|
||||
resolve(nodes);
|
||||
});
|
||||
},
|
||||
async init() {
|
||||
const data = await getChildRegion(0);
|
||||
this.data = data.result.map((item) => ({
|
||||
value: item.id,
|
||||
label: item.name,
|
||||
leaf: item.name === "台湾省",
|
||||
}));
|
||||
},
|
||||
async reviewData() {
|
||||
const addr = JSON.parse(JSON.stringify(this.addressId.split(",")));
|
||||
const length = addr.length;
|
||||
const root = await getChildRegion(0);
|
||||
const arr0 = root.result.map((item) => ({
|
||||
value: item.id,
|
||||
label: item.name,
|
||||
leaf: item.name === "台湾省",
|
||||
children: [],
|
||||
}));
|
||||
const props = defineProps(["addressId"]);
|
||||
const emit = defineEmits(["selected"]);
|
||||
|
||||
if (length > 0) {
|
||||
const children = await getChildRegion(addr[0]);
|
||||
const arr1 = this.handleData(children.result);
|
||||
arr0.forEach((e) => {
|
||||
if (e.value === addr[0]) {
|
||||
e.children = arr1;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (length > 1) {
|
||||
let arr1 = arr0.find((e) => e.value === addr[0])?.children || [];
|
||||
const children = await getChildRegion(addr[1]);
|
||||
const arr2 = this.handleData(children.result);
|
||||
arr1.forEach((e) => {
|
||||
if (e.value === addr[1]) {
|
||||
e.children = arr2;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (length > 2) {
|
||||
const arr1 = arr0.find((e) => e.value === addr[0])?.children || [];
|
||||
const arr2 = arr1.find((e) => e.value === addr[1])?.children || [];
|
||||
const children = await getChildRegion(addr[2]);
|
||||
const arr3 = this.handleData(children.result);
|
||||
arr2.forEach((e) => {
|
||||
if (e.value === addr[2]) {
|
||||
e.children = arr3;
|
||||
}
|
||||
});
|
||||
}
|
||||
this.data = arr0;
|
||||
this.addr = addr;
|
||||
},
|
||||
handleData(data) {
|
||||
return data.map((child) => ({
|
||||
const data = ref([]);
|
||||
const addr = ref([]);
|
||||
|
||||
const cascaderProps = computed(() => {
|
||||
return {
|
||||
value: "value",
|
||||
label: "label",
|
||||
lazy: true,
|
||||
lazyLoad: loadData,
|
||||
};
|
||||
});
|
||||
|
||||
function change(val) {
|
||||
if (!val || !val.length) {
|
||||
emit("selected", [[], []]);
|
||||
return;
|
||||
}
|
||||
const labels = resolveLabels(val);
|
||||
emit("selected", [val, labels]);
|
||||
}
|
||||
|
||||
function resolveLabels(values) {
|
||||
const labels = [];
|
||||
let options = data.value;
|
||||
for (const v of values) {
|
||||
const node = options.find((item) => item.value === v);
|
||||
if (!node) break;
|
||||
labels.push(node.label);
|
||||
options = node.children || [];
|
||||
}
|
||||
return labels;
|
||||
}
|
||||
|
||||
function loadData(node, resolve) {
|
||||
const parentId = node.level === 0 ? 0 : node.value;
|
||||
getChildRegion(parentId).then((res) => {
|
||||
if (!res.result || res.result.length <= 0) {
|
||||
resolve([]);
|
||||
return;
|
||||
}
|
||||
const nodes = res.result.map((child) => {
|
||||
const isLeaf =
|
||||
child.level === "street" ||
|
||||
node.label === "香港特别行政区" ||
|
||||
child.name === "台湾省";
|
||||
return {
|
||||
value: child.id,
|
||||
label: child.name,
|
||||
leaf: child.level === "street",
|
||||
children: child.level === "street" ? undefined : [],
|
||||
}));
|
||||
},
|
||||
leaf: isLeaf,
|
||||
};
|
||||
});
|
||||
resolve(nodes);
|
||||
});
|
||||
}
|
||||
|
||||
async function init() {
|
||||
const res = await getChildRegion(0);
|
||||
data.value = res.result.map((item) => ({
|
||||
value: item.id,
|
||||
label: item.name,
|
||||
leaf: item.name === "台湾省",
|
||||
}));
|
||||
}
|
||||
|
||||
async function reviewData() {
|
||||
const addrArr = JSON.parse(JSON.stringify(props.addressId.split(",")));
|
||||
const length = addrArr.length;
|
||||
const root = await getChildRegion(0);
|
||||
const arr0 = root.result.map((item) => ({
|
||||
value: item.id,
|
||||
label: item.name,
|
||||
leaf: item.name === "台湾省",
|
||||
children: [],
|
||||
}));
|
||||
|
||||
if (length > 0) {
|
||||
const children = await getChildRegion(addrArr[0]);
|
||||
const arr1 = handleData(children.result);
|
||||
arr0.forEach((e) => {
|
||||
if (e.value === addrArr[0]) {
|
||||
e.children = arr1;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (length > 1) {
|
||||
let arr1 = arr0.find((e) => e.value === addrArr[0])?.children || [];
|
||||
const children = await getChildRegion(addrArr[1]);
|
||||
const arr2 = handleData(children.result);
|
||||
arr1.forEach((e) => {
|
||||
if (e.value === addrArr[1]) {
|
||||
e.children = arr2;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (length > 2) {
|
||||
const arr1 = arr0.find((e) => e.value === addrArr[0])?.children || [];
|
||||
const arr2 = arr1.find((e) => e.value === addrArr[1])?.children || [];
|
||||
const children = await getChildRegion(addrArr[2]);
|
||||
const arr3 = handleData(children.result);
|
||||
arr2.forEach((e) => {
|
||||
if (e.value === addrArr[2]) {
|
||||
e.children = arr3;
|
||||
}
|
||||
});
|
||||
}
|
||||
data.value = arr0;
|
||||
addr.value = addrArr;
|
||||
}
|
||||
|
||||
function handleData(list) {
|
||||
return list.map((child) => ({
|
||||
value: child.id,
|
||||
label: child.name,
|
||||
leaf: child.level === "street",
|
||||
children: child.level === "street" ? undefined : [],
|
||||
}));
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.addressId,
|
||||
(v) => {
|
||||
if (v) {
|
||||
reviewData();
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
addressId: {
|
||||
handler(v) {
|
||||
if (v) {
|
||||
this.reviewData();
|
||||
} else {
|
||||
this.init();
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
@@ -37,107 +37,106 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, watch } from "vue";
|
||||
import { Refresh, DArrowRight } from "@element-plus/icons-vue";
|
||||
import { getVerifyImg, postVerifyImg } from "./verify.js";
|
||||
import { Message } from "@/utils/message";
|
||||
|
||||
export default {
|
||||
components: { Refresh, DArrowRight },
|
||||
props: {
|
||||
verifyType: {
|
||||
default: "LOGIN",
|
||||
type: String,
|
||||
},
|
||||
const props = defineProps({
|
||||
verifyType: {
|
||||
default: "LOGIN",
|
||||
type: String,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
type: "LOGIN",
|
||||
data: {
|
||||
backImage: "",
|
||||
slidingImage: "",
|
||||
originalHeight: 150,
|
||||
originalWidth: 300,
|
||||
sliderWidth: 60,
|
||||
sliderHeight: 60,
|
||||
},
|
||||
distance: 0,
|
||||
flag: false,
|
||||
downX: 0,
|
||||
bgColor: "#04ad11",
|
||||
verifyText: "拖动滑块解锁",
|
||||
};
|
||||
});
|
||||
const emit = defineEmits(["change"]);
|
||||
|
||||
const show = ref(false);
|
||||
const type = ref("LOGIN");
|
||||
const data = ref({
|
||||
backImage: "",
|
||||
slidingImage: "",
|
||||
originalHeight: 150,
|
||||
originalWidth: 300,
|
||||
sliderWidth: 60,
|
||||
sliderHeight: 60,
|
||||
});
|
||||
const distance = ref(0);
|
||||
const flag = ref(false);
|
||||
const downX = ref(0);
|
||||
const bgColor = ref("#04ad11");
|
||||
const verifyText = ref("拖动滑块解锁");
|
||||
|
||||
watch(
|
||||
() => props.verifyType,
|
||||
(v) => {
|
||||
type.value = v;
|
||||
},
|
||||
methods: {
|
||||
mouseDown(e) {
|
||||
this.downX = e.clientX;
|
||||
this.flag = true;
|
||||
},
|
||||
mouseMove(e) {
|
||||
if (this.flag) {
|
||||
const offset = e.clientX - this.downX;
|
||||
if (offset > this.data.originalWidth - 43) {
|
||||
this.distance = this.data.originalWidth - 43;
|
||||
} else if (offset < 0) {
|
||||
this.distance = 0;
|
||||
} else {
|
||||
this.distance = offset;
|
||||
}
|
||||
}
|
||||
},
|
||||
mouseUp() {
|
||||
if (!this.flag) return false;
|
||||
this.flag = false;
|
||||
const params = {
|
||||
verificationEnums: this.type,
|
||||
xPos: this.distance,
|
||||
};
|
||||
postVerifyImg(params)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
if (res.result) {
|
||||
this.bgColor = "green";
|
||||
this.verifyText = "解锁成功";
|
||||
this.$emit("change", { status: true, distance: this.distance });
|
||||
} else {
|
||||
this.bgColor = "red";
|
||||
this.verifyText = "解锁失败";
|
||||
setTimeout(() => this.init(), 1000);
|
||||
this.$emit("change", { status: false, distance: this.distance });
|
||||
}
|
||||
} else {
|
||||
this.init();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.init();
|
||||
});
|
||||
},
|
||||
init() {
|
||||
this.flag = false;
|
||||
this.downX = 0;
|
||||
this.distance = 0;
|
||||
this.bgColor = "#04ad11";
|
||||
this.verifyText = "拖动滑块解锁";
|
||||
getVerifyImg(this.type).then((res) => {
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
function mouseDown(e) {
|
||||
downX.value = e.clientX;
|
||||
flag.value = true;
|
||||
}
|
||||
function mouseMove(e) {
|
||||
if (flag.value) {
|
||||
const offset = e.clientX - downX.value;
|
||||
if (offset > data.value.originalWidth - 43) {
|
||||
distance.value = data.value.originalWidth - 43;
|
||||
} else if (offset < 0) {
|
||||
distance.value = 0;
|
||||
} else {
|
||||
distance.value = offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
function mouseUp() {
|
||||
if (!flag.value) return false;
|
||||
flag.value = false;
|
||||
const params = {
|
||||
verificationEnums: type.value,
|
||||
xPos: distance.value,
|
||||
};
|
||||
postVerifyImg(params)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
if (res.result) {
|
||||
this.data = res.result;
|
||||
this.show = true;
|
||||
bgColor.value = "green";
|
||||
verifyText.value = "解锁成功";
|
||||
emit("change", { status: true, distance: distance.value });
|
||||
} else {
|
||||
this.$Message.warning("请求失败请重试!");
|
||||
bgColor.value = "red";
|
||||
verifyText.value = "解锁失败";
|
||||
setTimeout(() => init(), 1000);
|
||||
emit("change", { status: false, distance: distance.value });
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
verifyType: {
|
||||
immediate: true,
|
||||
handler(v) {
|
||||
this.type = v;
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
init();
|
||||
});
|
||||
}
|
||||
function init() {
|
||||
flag.value = false;
|
||||
downX.value = 0;
|
||||
distance.value = 0;
|
||||
bgColor.value = "#04ad11";
|
||||
verifyText.value = "拖动滑块解锁";
|
||||
getVerifyImg(type.value).then((res) => {
|
||||
if (res.result) {
|
||||
data.value = res.result;
|
||||
show.value = true;
|
||||
} else {
|
||||
Message.warning("请求失败请重试!");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// login.vue 通过 verifyRef.value.show = false 关闭、verifyRef.value.init() 打开
|
||||
defineExpose({ show, init });
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
Reference in New Issue
Block a user