wangfukang 5 days ago
parent
commit
e6f6b6253b
  1. 4
      package.json
  2. 104
      src/views/app/shop/dictManage.vue
  3. 40
      src/views/app/shopArea/shopArea.vue
  4. 25
      src/views/sys/dict-manage/dictManage.vue
  5. 7
      src/views/sys/setting-manage/settingManage.vue
  6. 191
      src/views/sys/upApp/appUpload.vue
  7. 31
      src/views/sys/upApp/upApp.vue
  8. 2
      src/views/sys/user-manage/addEdit.vue

4
package.json

@ -9,8 +9,8 @@
"dev": "vue-cli-service serve",
"build": "vue-cli-service build",
"dev:nw": "nw",
"build:nw": "npm run build && build --tasks win-x86 --mirror https://npm.taobao.org/mirrors/nwjs/ .",
"build:nw:all": "npm run build && build --tasks win-x86,win-x64,linux-x86,linux-x64,mac-x64 --mirror https://npm.taobao.org/mirrors/nwjs/ ."
"build:nw": "npm run build && build --tasks win-x86 --mirror https://npmmirror.com/mirrors/nwjs/ .",
"build:nw:all": "npm run build && build --tasks win-x86,win-x64,linux-x86,linux-x64,mac-x64 --mirror https://npmmirror.com/mirrors/nwjs/ ."
},
"dependencies": {
"@mapbox/mapbox-gl-geocoder": "^5.0.0",

104
src/views/app/shop/dictManage.vue

@ -377,6 +377,26 @@
</div>
</FormItem>
</Col>
<Col span="8" v-if="addGoodsForm.isMoreBuy != 1">
<FormItem label="可搭配商品选择" class="form-noheight">
<Select
v-model="addGoodsForm.moreBuyProductIds"
multiple
filterable
transfer
:loading="moreBuyProductLoading"
placeholder="请选择可搭配商品"
@on-open-change="handleMoreBuySelectOpen"
>
<Option
v-for="item in moreBuyProductOptions"
:value="item.id"
:key="item.id"
:disabled="item.id == addGoodsForm.id"
>{{ item.productName }}</Option>
</Select>
</FormItem>
</Col>
<Col span="8">
<FormItem label="详细图文" class="form-noheight">
<div style="width:500px;height:400px;">
@ -541,6 +561,7 @@
}],
sellTime:0, //可售时间
isMoreBuy:0, //加料
moreBuyProductIds:[],
sellBeginTime:'', //可售时段自定义---开始时间
sellEndTime:'', //可售时段自定义---结束时间
attributeList:'',
@ -921,6 +942,8 @@
},
],
submitLoading: false, // 添加或编辑提交状态
moreBuyProductLoading: false,
moreBuyProductOptions: [],
data: [], //表单数据
total: 0, // 表单数据总数
screenWidth: 0,
@ -1044,6 +1067,48 @@
isRequestSuccess(res) {
return res && (res.success || res.code == 200);
},
normalizeMoreBuyProductIds(value) {
if (Array.isArray(value)) {
return value.filter(item => item !== "" && item !== null && item !== undefined).map(String);
}
if (value === "" || value === null || value === undefined) {
return [];
}
return String(value).split(",").map(item => item.trim()).filter(item => item);
},
getSubmitMoreBuyProductIds() {
if (Number(this.addGoodsForm.isMoreBuy) === 1) {
return "";
}
return this.normalizeMoreBuyProductIds(this.addGoodsForm.moreBuyProductIds).join(",");
},
handleMoreBuySelectOpen(open) {
if (open) {
this.getMoreBuyProductOptions();
}
},
getMoreBuyProductOptions() {
const shopId = this.shopId || this.addGoodsForm.shopId;
if (!shopId) {
this.moreBuyProductOptions = [];
return Promise.resolve();
}
this.moreBuyProductLoading = true;
return getShareList({
shopId: shopId,
isMoreBuy: 1,
pageNum: 1,
pageSize: 1000,
}).then((res) => {
this.moreBuyProductLoading = false;
if (this.isRequestSuccess(res) && res.result) {
const records = Array.isArray(res.result.records) ? res.result.records : [];
this.moreBuyProductOptions = records.filter(item => item.id != this.addGoodsForm.id);
}
}).catch(() => {
this.moreBuyProductLoading = false;
});
},
openAttributeManage() {
if (!this.attrId) {
this.$Message.warning("当前店铺未配置属性分类");
@ -1472,6 +1537,10 @@
this.addGoodsForm.isMoreBuy = type;
if (this.addGoodsForm.isMoreBuy == 1) {
this.addGoodsForm.lunchBox = 0;
this.addGoodsForm.moreBuyProductIds = [];
} else {
this.addGoodsForm.moreBuyProductIds = this.normalizeMoreBuyProductIds(this.addGoodsForm.moreBuyProductIds);
this.getMoreBuyProductOptions();
}
},
checkSpecValue(index){
@ -1757,6 +1826,7 @@
}],
sellTime:0, //可售时间
isMoreBuy:0, //加料
moreBuyProductIds:[],
sellBeginTime:'', //可售时段自定义---开始时间
sellEndTime:'', //可售时段自定义---结束时间
attributeList:'',
@ -1769,6 +1839,7 @@
this.addGoodsForm.shopId = this.shopId
this.addGoodsForm.categoryId = this.selectNode.id
this.addGoodsForm.categoryName = this.selectNode.categoryName
this.getMoreBuyProductOptions();
this.modalVisible = true;
},
edit(v) {
@ -1801,6 +1872,7 @@
if(this.addGoodsForm.isMoreBuy === '' || this.addGoodsForm.isMoreBuy === undefined || this.addGoodsForm.isMoreBuy === null){
this.addGoodsForm.isMoreBuy = 0
}
this.addGoodsForm.moreBuyProductIds = this.normalizeMoreBuyProductIds(this.addGoodsForm.moreBuyProductIds)
this.addGoodsForm.attributeList1 = JSON.parse(this.addGoodsForm.attributeList)
this.addGoodsForm.attributeList1 = new Map(Object.entries(this.addGoodsForm.attributeList1));
// 兼容历史数据:如果没有canbuy字段,默认设为1
@ -1823,6 +1895,9 @@
this.$set(this.addGoodsForm, "sellTime", this.addGoodsForm.sellBeginTime != "" ? 1 : 0)
this.setGoodsCategoryName(this.addGoodsForm.categoryId)
if (this.addGoodsForm.isMoreBuy != 1) {
this.getMoreBuyProductOptions();
}
this.modalVisible = true;
},
handelSubmit() {
@ -1836,14 +1911,16 @@
this.submitLoading = true;
if (this.modalType == 0) {
// 添加 避免编辑后传入id等数据 记得删除
delete this.addGoodsForm.id;
if(this.addGoodsForm.isMoreBuy != 1){
this.addGoodsForm.isMoreBuy = 0
const submitForm = Object.assign({}, this.addGoodsForm);
delete submitForm.id;
if(submitForm.isMoreBuy != 1){
submitForm.isMoreBuy = 0
}
this.addGoodsForm.attributeListPrice = JSON.stringify(this.addGoodsForm.attributeListPrice)
this.addGoodsForm.attributeList = Object.fromEntries(this.addGoodsForm.attributeList1)
this.addGoodsForm.attributeList = JSON.stringify(this.addGoodsForm.attributeList)
productSave(this.addGoodsForm).then((res) => {
submitForm.moreBuyProductIds = this.getSubmitMoreBuyProductIds()
submitForm.attributeListPrice = JSON.stringify(submitForm.attributeListPrice)
submitForm.attributeList = Object.fromEntries(submitForm.attributeList1)
submitForm.attributeList = JSON.stringify(submitForm.attributeList)
productSave(submitForm).then((res) => {
this.submitLoading = false;
if (res.success) {
this.$Message.success("操作成功");
@ -1853,12 +1930,13 @@
});
} else if (this.modalType == 1) {
// 编辑
this.addGoodsForm.attributeListPrice = JSON.stringify(this.addGoodsForm.attributeListPrice)
this.addGoodsForm.attributeList = Object.fromEntries(this.addGoodsForm.attributeList1)
this.addGoodsForm.attributeList = JSON.stringify(this.addGoodsForm.attributeList)
this.addGoodsForm.productPictures = []
productEdit(this.addGoodsForm).then((res) => {
const submitForm = Object.assign({}, this.addGoodsForm);
submitForm.moreBuyProductIds = this.getSubmitMoreBuyProductIds()
submitForm.attributeListPrice = JSON.stringify(submitForm.attributeListPrice)
submitForm.attributeList = Object.fromEntries(submitForm.attributeList1)
submitForm.attributeList = JSON.stringify(submitForm.attributeList)
submitForm.productPictures = []
productEdit(submitForm).then((res) => {
this.submitLoading = false;
if (res.success) {
this.$Message.success("操作成功");

40
src/views/app/shopArea/shopArea.vue

@ -172,6 +172,18 @@
</RadioGroup>
<div class="geo-tip">社会区域首页展示待成团,可单独配置快递/跑腿入口</div>
</FormItem>
<FormItem v-if="showEditParentAgent && Number(form.areaKind) === 2" label="文案按照校园显示">
<i-switch
size="large"
v-model="form.campusTextEnabled"
:true-value="1"
:false-value="0"
>
<span slot="open">开启</span>
<span slot="close">关闭</span>
</i-switch>
<div class="geo-tip">开启后社会区域用户端仅文案按校园区域展示,业务逻辑仍按社会区域处理</div>
</FormItem>
<FormItem v-if="showEditParentAgent && Number(form.areaKind) === 2" label="快递/跑腿">
<i-switch
size="large"
@ -336,6 +348,18 @@
</RadioGroup>
<div class="geo-tip">社会区域首页展示待成团,可单独配置快递/跑腿入口</div>
</FormItem>
<FormItem v-if="showAddParentAgent && Number(formAdd.areaKind) === 2" label="文案按照校园显示">
<i-switch
size="large"
v-model="formAdd.campusTextEnabled"
:true-value="1"
:false-value="0"
>
<span slot="open">开启</span>
<span slot="close">关闭</span>
</i-switch>
<div class="geo-tip">开启后社会区域用户端仅文案按校园区域展示,业务逻辑仍按社会区域处理</div>
</FormItem>
<FormItem v-if="showAddParentAgent && Number(formAdd.areaKind) === 2" label="快递/跑腿">
<i-switch
size="large"
@ -530,6 +554,7 @@ export default {
parentAgentName: "",
commissionRatio: 0,
areaKind: 1,
campusTextEnabled: 0,
expressErrandEnabled: 1,
errandPlanetVisible: 1,
centerLng: null,
@ -813,6 +838,7 @@ export default {
this.$delete(target, "commissionRatio");
this.clearGeoValue(target);
this.$set(target, "areaKind", 1);
this.$set(target, "campusTextEnabled", 0);
this.$set(target, "expressErrandEnabled", 1);
this.$set(target, "errandPlanetVisible", 1);
} else {
@ -827,6 +853,11 @@ export default {
}
const areaKind = Number(target.areaKind);
this.$set(target, "areaKind", areaKind === 2 ? 2 : 1);
this.$set(
target,
"campusTextEnabled",
areaKind === 2 && Number(target.campusTextEnabled) === 1 ? 1 : 0
);
this.$set(
target,
"expressErrandEnabled",
@ -972,6 +1003,11 @@ export default {
}
const areaKind = Number(this.form.areaKind);
this.$set(this.form, "areaKind", areaKind === 2 ? 2 : 1);
this.$set(
this.form,
"campusTextEnabled",
areaKind === 2 && Number(this.form.campusTextEnabled) === 1 ? 1 : 0
);
this.$set(
this.form,
"expressErrandEnabled",
@ -1005,6 +1041,7 @@ export default {
parentAgentName: "",
commissionRatio: 0,
areaKind: 1,
campusTextEnabled: 0,
expressErrandEnabled: 1,
errandPlanetVisible: 1,
centerLng: null,
@ -1115,6 +1152,7 @@ export default {
status: 0,
parentAgentId: "",
parentAgentName: "",
campusTextEnabled: 0,
expressErrandEnabled: 1,
errandPlanetVisible: 1,
};
@ -1137,6 +1175,7 @@ export default {
status: 0,
parentAgentId: "",
parentAgentName: "",
campusTextEnabled: 0,
expressErrandEnabled: 1,
errandPlanetVisible: 1,
};
@ -1158,6 +1197,7 @@ export default {
parentAgentName: "",
commissionRatio: 0,
areaKind: 1,
campusTextEnabled: 0,
expressErrandEnabled: 1,
errandPlanetVisible: 1,
centerLng: null,

25
src/views/sys/dict-manage/dictManage.vue

@ -114,6 +114,13 @@
<FormItem label="数据值" prop="value">
<Input v-model="form.value" />
</FormItem>
<FormItem label="分类用途" prop="merchantType">
<Select v-model="form.merchantType" placeholder="请选择分类用途">
<Option :value="0">不区分</Option>
<Option :value="1">外卖分类</Option>
<Option :value="2">团购分类</Option>
</Select>
</FormItem>
<FormItem label="备注" prop="description">
<Input v-model="form.description" />
</FormItem>
@ -200,6 +207,7 @@
// 添加或编辑表单对象初始化数据
title: "",
value: "",
merchantType: 0,
status: 0,
description: "",
sortOrder: 0,
@ -247,6 +255,20 @@
minWidth: 160,
sortable: true,
},
{
title: "分类用途",
key: "merchantType",
width: 120,
align: "center",
render: (h, params) => {
const typeMap = {
0: "不区分",
1: "外卖分类",
2: "团购分类",
};
return h("span", typeMap[params.row.merchantType] || "-");
},
},
{
title: "备注",
key: "description",
@ -570,6 +592,7 @@
this.modalTitle = "添加字典 " + this.editTitle + " 的数据";
this.$refs.form.resetFields();
this.form.sortOrder = this.data.length + 1;
this.form.merchantType = 0;
this.modalVisible = true;
},
edit(v) {
@ -588,6 +611,7 @@
}
let str = JSON.stringify(v);
let data = JSON.parse(str);
data.merchantType = Number(data.merchantType || 0);
this.form = data;
this.modalVisible = true;
},
@ -595,6 +619,7 @@
this.$refs.form.validate((valid) => {
if (valid) {
this.submitLoading = true;
this.form.merchantType = Number(this.form.merchantType || 0);
if (this.modalType == 0) {
// 添加 避免编辑后传入id等数据 记得删除
delete this.form.id;

7
src/views/sys/setting-manage/settingManage.vue

@ -23,6 +23,7 @@
<MenuItem name="audit">审核管理</MenuItem>
<MenuItem name="notice">系统公告配置</MenuItem>
<MenuItem name="appUpdate">APP热更新</MenuItem>
<MenuItem name="appUpload">APP上传</MenuItem>
<MenuItem name="other">其他配置</MenuItem>
</Menu>
</Sider>
@ -220,6 +221,10 @@
<div class="setting-title">APP热更新</div>
<up-app />
</div>
<div v-show="currName == 'appUpload'">
<div class="setting-title">APP上传</div>
<app-upload />
</div>
<div v-show="currName == 'other'">
<div class="setting-title">其他配置</div>
<div style="display: flex">
@ -300,6 +305,7 @@ import sms from "./sms.vue";
import stopWord from "./stopWord.vue";
import protocol from "./protocol.vue";
import upApp from "../upApp/upApp.vue";
import appUpload from "../upApp/appUpload.vue";
export default {
name: "setting-manage",
components: {
@ -308,6 +314,7 @@ export default {
stopWord,
protocol,
upApp,
appUpload,
},
data() {
return {

191
src/views/sys/upApp/appUpload.vue

@ -0,0 +1,191 @@
<template>
<div style="position: relative">
<Upload
:action="uploadFileUrl"
:headers="accessToken"
:on-success="handleSuccess"
:on-error="handleError"
:max-size="512000"
:on-exceeded-size="handleMaxSize"
:before-upload="beforeUpload"
:format="['apk', 'ipa']"
:on-format-error="handleFormatError"
type="drag"
ref="up"
>
<div style="padding: 20px 0">
<Icon type="ios-cloud-upload" size="52" style="color: #3399ff"></Icon>
<p>点击这里或将APP安装包拖拽到这里上传</p>
<p style="color: #999; margin-top: 8px">支持 apk、ipa 格式,最大 500MB</p>
</div>
</Upload>
<Form :model="form" :label-width="100" style="margin-top: 24px">
<FormItem label="原始链接">
<Input
v-model="form.url"
readonly
placeholder="请先上传APP安装包"
style="width: 520px"
/>
</FormItem>
<FormItem label="CNAME域名">
<Input
v-model="cnameDomain"
placeholder="请输入已绑定OSS的自定义域名,如 https://download.example.com"
style="width: 520px"
/>
</FormItem>
<FormItem label="官网链接">
<Input
:value="downloadUrl"
readonly
placeholder="填写CNAME域名后自动生成可用于官网的下载链接"
style="width: 520px"
/>
<Button
type="primary"
icon="md-copy"
style="margin-left: 10px"
:disabled="!downloadUrl"
@click="copyUrl"
>
复制链接
</Button>
</FormItem>
<FormItem label="文件名">
<Input v-model="form.fileName" readonly style="width: 380px" />
</FormItem>
</Form>
<Alert type="info" show-icon style="width: 650px">
阿里云限制 APK 不能直接使用 OSS endpoint 公网分发,请先把自定义域名 CNAME 到 OSS 并在 OSS 控制台绑定,再复制官网链接替换 index.html 中的 APP_DOWNLOAD_URL。
</Alert>
<Spin fix v-if="loading"></Spin>
</div>
</template>
<script>
import {
uploadFile,
checkOssSet,
} from "@/api/index";
export default {
name: "appUpload",
data() {
return {
uploadFileUrl: uploadFile,
loading: false,
accessToken: {},
cnameDomain: "https://static.hbkuaishi.com",
form: {
url: "",
fileName: "",
},
};
},
computed: {
downloadUrl() {
return this.buildCnameUrl(this.form.url);
},
},
methods: {
init() {
this.accessToken = {
accessToken: this.getStore("accessToken"),
};
checkOssSet().then((res) => {
if (!res.success) {
this.$Modal.confirm({
title: "系统还未配置OSS",
content: "管理员还未配置第三方OSS服务,是否现在立即去配置?",
onOk: () => {
this.$router.push({
name: "setting",
query: {
name: "oss",
},
});
},
});
}
});
},
beforeUpload(file) {
const name = file.name ? file.name.toLowerCase() : "";
if (!name.endsWith(".apk") && !name.endsWith(".ipa")) {
this.$Message.error("只能上传apk或ipa格式的APP安装包");
return false;
}
this.loading = true;
return true;
},
handleSuccess(res, file, fileList) {
this.loading = false;
if (res.success) {
this.$Message.success("上传文件 " + file.name + " 成功");
this.form.url = res.result;
this.form.fileName = file.name;
} else {
file.percentage = 0;
file.status = "fail";
file.name += "(上传失败)";
this.$Message.error(res.message);
}
},
handleError(error, file, fileList) {
this.loading = false;
this.$Message.error(error.toString());
},
handleFormatError(file) {
this.$Message.error(file.name + " 不是apk或ipa格式文件");
},
handleMaxSize(file) {
this.loading = false;
this.$Notice.warning({
title: "文件大小过大",
desc: "文件 " + file.name + " 超过 500MB,请压缩或重新选择",
});
},
buildCnameUrl(url) {
const domain = this.normalizeDomain(this.cnameDomain);
if (!url || !domain) {
return "";
}
try {
const parsed = new URL(url);
return domain + parsed.pathname + parsed.search + parsed.hash;
} catch (e) {
const path = url.charAt(0) === "/" ? url : "/" + url;
return domain + path;
}
},
normalizeDomain(domain) {
let value = domain ? domain.trim() : "";
if (!value) {
return "";
}
value = value.replace(/\/+$/, "");
if (!/^https?:\/\//i.test(value)) {
value = "https://" + value;
}
return value;
},
copyUrl() {
if (!this.downloadUrl) {
this.$Message.warning("请先上传APP安装包并填写CNAME域名");
return;
}
this.$copyText(this.downloadUrl).then(() => {
this.$Message.success("复制链接成功");
}, () => {
this.$Message.warning("复制链接失败,请手动复制");
});
},
},
mounted() {
this.init();
},
};
</script>
<style lang="less">
</style>

31
src/views/sys/upApp/upApp.vue

@ -15,6 +15,9 @@
<FormItem label="wgt包地址" prop="url">
<Input v-model="form.url" readonly placeholder="请先上传wgt包" style="width: 520px" />
</FormItem>
<FormItem label="CNAME域名">
<Input v-model="cnameDomain" placeholder="请输入已绑定OSS的自定义域名" style="width: 520px" />
</FormItem>
<FormItem label="文件名">
<Input v-model="form.fileName" readonly style="width: 380px" />
</FormItem>
@ -42,6 +45,7 @@
submitLoading: false,
accessToken: {},
uploadData: {},
cnameDomain: "https://static.hbkuaishi.com",
form:{
url: "",
version: "",
@ -87,6 +91,7 @@
}
}
this.form = res.result;
this.form.url = this.buildCnameUrl(this.form.url);
}
});
},
@ -100,7 +105,7 @@
handleSuccess(res, file, fileList) {
if (res.success) {
this.$Message.success("上传文件 " + file.name + " 成功");
this.form.url = res.result;
this.form.url = this.buildCnameUrl(res.result);
this.form.fileName = file.name;
} else {
file.percentage = 0;
@ -115,6 +120,30 @@
handleFormatError(file) {
this.$Message.error(file.name + " 不是wgt格式文件");
},
buildCnameUrl(url) {
const domain = this.normalizeDomain(this.cnameDomain);
if (!url || !domain) {
return url || "";
}
try {
const parsed = new URL(url);
return domain + parsed.pathname + parsed.search + parsed.hash;
} catch (e) {
const path = url.charAt(0) === "/" ? url : "/" + url;
return domain + path;
}
},
normalizeDomain(domain) {
let value = domain ? domain.trim() : "";
if (!value) {
return "";
}
value = value.replace(/\/+$/, "");
if (!/^https?:\/\//i.test(value)) {
value = "https://" + value;
}
return value;
},
handelSubmit() {
this.$refs.form.validate((valid) => {
if (!valid) {

2
src/views/sys/user-manage/addEdit.vue

@ -274,6 +274,8 @@ export default {
params.birth = this.format(params.birth, "yyyy-MM-dd");
}
delete params.realNameAuthTime;
delete params.inviteRewardTime;
delete params.inviteTransferAuthTime;
if (this.type == "1") {
// 编辑
this.submitLoading = true;

Loading…
Cancel
Save