This commit is contained in:
全栈小学生 2026-01-13 09:16:43 +08:00
parent e566d8d05c
commit abaa4201fb
1074 changed files with 138135 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,282 @@
import request from '@/utils/request'
/********************************* 物流公司 ***************************************/
/**
*
* @param params
* @returns
*/
export function getCompanyPageList(params: Record<string, any>) {
return request.get(`shop/delivery/company`, { params })
}
/**
*
* @param params
* @returns
*/
export function getCompanyList(params: Record<string, any>) {
return request.get(`shop/delivery/company/list`, { params })
}
/**
*
* @param company_id company_id
* @returns
*/
export function getCompanyInfo(company_id: number) {
return request.get(`shop/delivery/company/${ company_id }`);
}
/**
*
* @param params
* @returns
*/
export function addCompany(params: Record<string, any>) {
return request.post('shop/delivery/company', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function editCompany(params: Record<string, any>) {
return request.put(`shop/delivery/company/${ params.company_id }`, params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param company_id
* @returns
*/
export function deleteCompany(company_id: number) {
return request.delete(`shop/delivery/company/${ company_id }`, { showErrorMessage: true, showSuccessMessage: true })
}
/********************************* 运费模版 ***************************************/
/**
*
* @param params
* @returns
*/
export function getShippingTemplatePageList(params: Record<string, any>) {
return request.get(`shop/shipping/template`, { params })
}
/**
*
* @param params
* @returns
*/
export function getShippingTemplateList(params: Record<string, any>) {
return request.get(`shop/shipping/template/list`, { params })
}
/**
*
* @param template_id template_id
* @returns
*/
export function getShippingTemplateInfo(template_id: number) {
return request.get(`shop/shipping/template/${ template_id }`);
}
/**
*
* @param params
* @returns
*/
export function addShippingTemplate(params: Record<string, any>) {
return request.post('shop/shipping/template', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function editShippingTemplate(params: Record<string, any>) {
return request.put(`shop/shipping/template/${ params.template_id }`, params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param template_id
* @returns
*/
export function deleteShippingTemplate(template_id: number) {
return request.delete(`shop/shipping/template/${ template_id }`, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/********************************* 自提门店 ***************************************/
/**
*
* @param params
* @returns
*/
export function getStoreList(params: Record<string, any>) {
return request.get(`shop/delivery/store`, { params })
}
/**
*
* @param store_id store_id
* @returns
*/
export function getStoreInfo(store_id: number) {
return request.get(`shop/delivery/store/${ store_id }`);
}
/**
*
* @returns
*/
export function getStoreInit() {
return request.get(`shop/delivery/store/init`)
}
/**
*
* @param params
* @returns
*/
export function addStore(params: Record<string, any>) {
return request.post('shop/delivery/store', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function editStore(params: Record<string, any>) {
return request.put(`shop/delivery/store/${ params.store_id }`, params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param store_id
* @returns
*/
export function deleteStore(store_id: number) {
return request.delete(`shop/delivery/store/${ store_id }`, { showErrorMessage: true, showSuccessMessage: true })
}
/********************************* 物流查询 ***************************************/
/**
*
* @param params
* @returns
*/
export function setDeliverySearch(params: Record<string, any>) {
return request.post('shop/delivery/search', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @returns
*/
export function getDeliverySearch() {
return request.get('shop/delivery/search')
}
/**
*
* @returns
*/
export function getShopDeliveryList() {
return request.get('shop/delivery/deliveryList')
}
/**
*
* @param params
* @returns
*/
export function setShopDeliveryConfig(params: Record<string, any>) {
return request.put(`shop/delivery/setConfig`, params, { showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function getShopDelivery(params: Record<string, any>) {
return request.get('shop/delivery/staff', { params })
}
/**
*
* @param staff_id
* @returns
*/
export function getShopDeliverInfo(staff_id: number) {
return request.get(`shop/delivery/staff/${ staff_id }`);
}
/**
*
* @param params
* @returns
*/
export function addShopDeliver(params: Record<string, any>) {
return request.post('shop/delivery/staff', params, { showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function editShopDeliver(params: Record<string, any>) {
return request.put(`shop/delivery/staff/${ params.deliver_id }`, params, { showSuccessMessage: true })
}
/**
*
* @param staff_id
* @returns
*/
export function deleteShopDeliver(staff_id: number) {
return request.delete(`shop/delivery/staff/${ staff_id }`)
}
/**
*
* @returns
*/
export function getLocal() {
return request.get('shop/local');
}
/**
*
* @param params
* @returns
*/
export function setLocal(params: Record<string, any>) {
return request.put('shop/local', params, { showSuccessMessage: true })
}
/**
*
* @returns
*/
export function getThird() {
return request.get('shop/third/init')
}

View File

@ -0,0 +1,116 @@
import request from '@/utils/request'
/**
*
* @param params
* @returns
*/
export function getElectronicSheetPageList(params: Record<string, any>) {
return request.get(`shop/electronic_sheet`, { params })
}
/**
*
* @param params
* @returns
*/
export function getElectronicSheetList(params: Record<string, any>) {
return request.get(`shop/electronic_sheet/list`, { params })
}
/**
*
* @param id id
* @returns
*/
export function getElectronicSheetInfo(id: number) {
return request.get(`shop/electronic_sheet/${ id }`);
}
/**
*
* @param params
* @returns
*/
export function addElectronicSheet(params: Record<string, any>) {
return request.post('shop/electronic_sheet', params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param params
* @returns
*/
export function editElectronicSheet(params: Record<string, any>) {
return request.put(`shop/electronic_sheet/${ params.id }`, params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param id
* @returns
*/
export function deleteElectronicSheet(id: number) {
return request.delete(`shop/electronic_sheet/${ id }`, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param params
* @returns
*/
export function setDefaultElectronicSheet(params: Record<string, any>) {
return request.put(`shop/electronic_sheet/setDefault/${ params.id }`, params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param params
* @returns
*/
export function setElectronicSheetConfig(params: Record<string, any>) {
return request.post('shop/electronic_sheet/config', params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @returns
*/
export function getElectronicSheetConfig() {
return request.get(`shop/electronic_sheet/config`)
}
/**
*
* @returns
*/
export function getElectronicSheetPayType() {
return request.get(`shop/electronic_sheet/paytype`)
}
/**
*
* @param params
* @returns
*/
export function printElectronicSheet(params: Record<string, any>) {
return request.post('shop/electronic_sheet/print', params, {
showErrorMessage: true,
showSuccessMessage: true
})
}

View File

@ -0,0 +1,862 @@
import request from '@/utils/request'
/**
*
* @param params
* @returns
*/
export function getGoodsPageList(params: Record<string, any>) {
return request.get(`shop/goods`, { params })
}
/**
*
* @param goods_id goods_id
* @returns
*/
export function getGoodsInfo(goods_id: number) {
return request.get(`shop/goods/${ goods_id }`);
}
/**
*
* @param goods_id goods_id
* @returns
*/
export function getGoodsInfoTemplate(params: any) {
return request.get(`diy/list`, { params });
}
/**
*
* @param params
* @returns
*/
export function addGoods(params: Record<string, any>) {
return request.post('shop/goods', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
*/
export function editGoods(params: Record<string, any>) {
return request.put(`shop/goods/${ params.goods_id }`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
* /
* @param params
*/
export function getGoodsInit(params: Record<string, any>) {
return request.get(`shop/goods/init`, { params });
}
/**
*
* @param params
* @returns
*/
export function addVirtualGoods(params: Record<string, any>) {
return request.post('shop/goods/virtual', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
*/
export function editVirtualGoods(params: Record<string, any>) {
return request.put(`shop/goods/virtual/${ params.goods_id }`, params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
* /
* @param params
*/
export function getVirtualGoodsInit(params: Record<string, any>) {
return request.get(`shop/goods/virtual/init`, { params });
}
/**
*
* @param params
* @returns
*/
export function deleteGoods(params: Record<string, any>) {
return request.put(`shop/goods/delete`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function getRecycleGoodsPageList(params: Record<string, any>) {
return request.get(`shop/goods/recycle`, { params })
}
/**
*
* @param params
* @returns
*/
export function recycleGoods(params: Record<string, any>) {
return request.put(`shop/goods/recycle`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
*/
export function editGoodsSort(params: Record<string, any>) {
return request.put(`shop/goods/sort`, params, { showSuccessMessage: true })
}
/**
*
* @param params
*/
export function editGoodsStatus(params: Record<string, any>) {
return request.put(`shop/goods/status`, params, { showSuccessMessage: true })
}
/**
*
* @param params
*/
export function copyGoods(params: Record<string, any>) {
return request.put(`shop/goods/copy/${ params.goods_id }`, params, { showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function getGoodsSelectPageList(params: Record<string, any>) {
return request.get(`shop/goods/select`, { params })
}
/**
* SKU规格列表
* @param params
* @returns
*/
export function getGoodsSkuList(params: Record<string, any>) {
return request.get(`shop/goods/sku`, { params })
}
/**
* SKU规格不分页列表
* @param params
* @returns
*/
export function getGoodsSkuNoPageList(params: Record<string, any>) {
return request.get(`shop/goods/selectgoodssku`, { params })
}
/**
* @param params
* @returns
*/
export function getActiveGoodsCount(params: Record<string, any>) {
return request.get(`shop/goods/active/count`, { params })
}
/**
* SKU规格库存
* @param params
* @returns
*/
export function editGoodsListStock(params: Record<string, any>) {
return request.put(`shop/goods/sku/stock`, params, { showSuccessMessage: true })
}
/**
* SKU规格价格
* @param params
* @returns
*/
export function editGoodsListPrice(params: Record<string, any>) {
return request.put(`shop/goods/sku/price`, params, { showSuccessMessage: true })
}
/**
* SKU规格会员价格
* @param params
* @returns
*/
export function editGoodsListMemberPrice(params: Record<string, any>) {
return request.put(`shop/goods/sku/member_price`, params, { showSuccessMessage: true })
}
/**
*
* @returns
*/
export function getGoodsType() {
return request.get(`shop/goods/type`);
}
/**
*
* @param params
* @returns
*/
export function getLabelPageList(params: Record<string, any>) {
return request.get(`shop/goods/label`, { params })
}
/**
*
* @param params
* @returns
*/
export function getLabelList(params: Record<string, any>) {
return request.get(`shop/goods/label/list`, { params })
}
/**
*
* @param label_id label_id
* @returns
*/
export function getLabelInfo(label_id: number) {
return request.get(`shop/goods/label/${ label_id }`);
}
/**
*
* @param params
* @returns
*/
export function addLabel(params: Record<string, any>) {
return request.post('shop/goods/label', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function editLabel(params: Record<string, any>) {
return request.put(`shop/goods/label/${ params.label_id }`, params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param params
*/
export function modifyLabelStatus(params: Record<string, any>) {
return request.put(`shop/goods/label/status`, params, { showSuccessMessage: true })
}
/**
*
* @param label_id
* @returns
*/
export function deleteLabel(label_id: number) {
return request.delete(`shop/goods/label/${ label_id }`, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
*/
export function modifyLabelSort(params: Record<string, any>) {
return request.put(`shop/goods/label/sort`, params, { showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function getLabelGroupPageList(params: Record<string, any>) {
return request.get(`shop/goods/label/group`, { params })
}
/**
*
* @param params
* @returns
*/
export function getLabelGroupList(params: Record<string, any>) {
return request.get(`shop/goods/label/group/list`, { params })
}
/**
*
* @param label_id label_id
* @returns
*/
export function getLabelGroupInfo(label_id: number) {
return request.get(`shop/goods/label/group/${ label_id }`);
}
/**
*
* @param params
* @returns
*/
export function addLabelGroup(params: Record<string, any>) {
return request.post('shop/goods/label/group', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function editLabelGroup(params: Record<string, any>) {
return request.put(`shop/goods/label/group/${ params.group_id }`, params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param group_id
* @returns
*/
export function deleteLabelGroup(group_id: number) {
return request.delete(`shop/goods/label/group/${ group_id }`, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
*/
export function modifyLabelGroupSort(params: Record<string, any>) {
return request.put(`shop/goods/label/group/sort`, params, { showSuccessMessage: true })
}
/**
*
* @param params
*/
export function copyLabel(params: Record<string, any>) {
return request.post(`shop/goods/label/copy/${ params.label_id }`, params, { showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function getBrandPageList(params: Record<string, any>) {
return request.get(`shop/goods/brand`, { params })
}
/**
*
* @param params
* @returns
*/
export function getBrandList(params: Record<string, any>) {
return request.get(`shop/goods/brand/list`, { params })
}
/**
*
* @param brand_id brand_id
* @returns
*/
export function getBrandInfo(brand_id: number) {
return request.get(`shop/goods/brand/${ brand_id }`);
}
/**
*
* @param params
* @returns
*/
export function addBrand(params: Record<string, any>) {
return request.post('shop/goods/brand', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function editBrand(params: Record<string, any>) {
return request.put(`shop/goods/brand/${ params.brand_id }`, params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param params
*/
export function modifyBrandSort(params: Record<string, any>) {
return request.put(`shop/goods/brand/sort`, params, { showSuccessMessage: true })
}
/**
*
* @param brand_id
* @returns
*/
export function deleteBrand(brand_id: number) {
return request.delete(`shop/goods/brand/${ brand_id }`, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function getServePageList(params: Record<string, any>) {
return request.get(`shop/goods/service`, { params })
}
/**
*
* @param params
* @returns
*/
export function getServeList(params: Record<string, any>) {
return request.get(`shop/goods/service/list`, { params })
}
/**
*
* @param service_id service_id
* @returns
*/
export function getServeInfo(service_id: number) {
return request.get(`shop/goods/service/${ service_id }`);
}
/**
*
* @param params
* @returns
*/
export function addServe(params: Record<string, any>) {
return request.post('shop/goods/service', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function editServe(params: Record<string, any>) {
return request.put(`shop/goods/service/${ params.service_id }`, params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param service_id
* @returns
*/
export function deleteServe(service_id: number) {
return request.delete(`shop/goods/service/${ service_id }`, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @returns
*/
export function getCategoryTree() {
return request.get(`shop/goods/tree`)
}
/**
*
* @param params
* @returns
*/
export function getCategoryList(params: Record<string, any>) {
return request.get(`shop/goods/category`, { params })
}
/**
*
* @param category_id category_id
* @returns
*/
export function getCategoryInfo(category_id: number) {
return request.get(`shop/goods/category/${ category_id }`);
}
/**
*
* @param params
* @returns
*/
export function addCategory(params: Record<string, any>) {
return request.post('shop/goods/category', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function editCategory(params: Record<string, any>) {
return request.put(`shop/goods/category/${ params.category_id }`, params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param category_id
* @returns
*/
export function deleteCategory(category_id: number) {
return request.delete(`shop/goods/category/${ category_id }`, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function updateCategory(params: Record<string, any>) {
return request.post(`shop/goods/category/update`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function setCategoryConfig(params: Record<string, any>) {
return request.post(`shop/goods/category/config`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @returns
*/
export function getCategoryConfig() {
return request.get(`shop/goods/category/config`);
}
/**
*
* @returns
*/
export function getCategoryTreeComponents() {
return request.get(`shop/goods/category/components`)
}
/**
*
* @param params
* @returns
*/
export function getSupplierList(params: Record<string, any>) {
return request.get(`shop_supplier/supplier/list`, { params })
}
/**
*
* @param params
* @returns
*/
export function getEvaluateList(params: Record<string, any>) {
return request.get(`shop/goods/evaluate`, { params })
}
/**
*
* @param params
* @returns
*/
export function addEvaluate(params: Record<string, any>) {
return request.post('shop/goods/evaluate', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param evaluate_id
* @returns
*/
export function deleteEvaluate(evaluate_id: number) {
return request.delete(`shop/goods/evaluate/${ evaluate_id }`, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param evaluate_id
* @returns
*/
export function adoptEvaluate(evaluate_id: number) {
return request.put(`shop/goods/evaluate/adopt/${ evaluate_id }`, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param evaluate_id
* @returns
*/
export function refuseEvaluate(evaluate_id: number) {
return request.put(`shop/goods/evaluate/refuse/${ evaluate_id }`, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param params
* @returns
*/
export function replyEvaluate(params: Record<string, any>) {
return request.put(`shop/goods/evaluate/reply/${ params.evaluate_id }`, params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param evaluate_id
* @returns
*/
export function toppingEvaluate(evaluate_id: number) {
return request.put(`shop/goods/evaluate/topping/${ evaluate_id }`, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param evaluate_id
* @returns
*/
export function cancelToppingEvaluate(evaluate_id: number) {
return request.put(`shop/goods/evaluate/cancel_topping/${ evaluate_id }`, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param params
* @returns
*/
export function getEvaluateStatus() {
return request.get(`shop/goods/evaluate/status`)
}
/**
*
* @param params
* @returns
*/
export function batchDelEvaluate(params: Record<string, any>) {
return request.post(`shop/goods/evaluate/batch/del`,params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param params
* @returns
*/
export function batchAdoptEvaluate(params: Record<string, any>) {
return request.post(`shop/goods/evaluate/batch/adopt`,params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param params
* @returns
*/
export function batchRefuseEvaluate(params: Record<string, any>) {
return request.post(`shop/goods/evaluate/batch/refuse`,params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param params
* @returns
*/
export function getAttrPageList(params: Record<string, any>) {
return request.get(`shop/goods/attr`, { params })
}
/**
*
* @param params
* @returns
*/
export function getAttrList(params: Record<string, any>) {
return request.get(`shop/goods/attr/list`, { params })
}
/**
*
* @param attr_id attr_id
* @returns
*/
export function getAttrInfo(attr_id: number) {
return request.get(`shop/goods/attr/${ attr_id }`);
}
/**
*
* @param params
* @returns
*/
export function addAttr(params: Record<string, any>) {
return request.post('shop/goods/attr', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function editAttr(params: Record<string, any>) {
return request.put(`shop/goods/attr/${ params.attr_id }`, params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param attr_id
* @returns
*/
export function deleteAttr(attr_id: number) {
return request.delete(`shop/goods/attr/${ attr_id }`, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
*/
export function modifyAttrSort(params: Record<string, any>) {
return request.put(`shop/goods/attr/sort`, params, { showSuccessMessage: true })
}
/**
*
* @param params
*/
export function modifyAttrName(params: Record<string, any>) {
return request.put(`shop/goods/attr/attr_name`, params, { showSuccessMessage: true })
}
/**
*
* @param params
*/
export function modifyAttrValue(params: Record<string, any>) {
return request.put(`shop/goods/attr/attr_value`, params, { showSuccessMessage: true })
}
/**
*
* @returns
*/
export function getGoodsBatchSetDict() {
return request.get(`shop/goods/batchSet/dict`)
}
/**
*
* @param params
*/
export function goodsBatchSet(params: Record<string, any>) {
return request.put(`shop/goods/batchSet`, params, { showSuccessMessage: true })
}
/**
*
* @returns
*/
export function getGoodsConfigSearch() {
return request.get(`shop/goods/config/search`)
}
/**
*
* @returns
*/
export function setGoodsConfigSearch(param: any) {
return request.post('shop/goods/config/search', param, { showSuccessMessage: true })
}
/**
*
* @returns
*/
export function getGoodsConfigUnique() {
return request.get(`shop/goods/config/unique`)
}
/**
*
* @returns
*/
export function setGoodsConfigUnique(param: any) {
return request.post('shop/goods/config/unique', param, { showSuccessMessage: true })
}
/**
*
* @returns
*/
export function goodsVerify(param: any) {
return request.post('shop/goods/verify/skuno', param)
}
/**
*
* @returns
*/
export function getGoodsConfigSort() {
return request.get(`shop/goods/config/sort`)
}
/**
*
* @returns
*/
export function setGoodsConfigSort(param: any) {
return request.post('shop/goods/config/sort', param, { showSuccessMessage: true })
}
// 上下架
export function editGoodssingleStatus(params: Record<string, any>) {
return request.put(`shop/goods/single/status`, params, { showSuccessMessage: true })
}

View File

@ -0,0 +1,692 @@
import request from '@/utils/request'
/**
*
* @param params
* @returns
*/
export function getMarketingIndex(params: Record<string, any>) {
return request.get(`shop/marketing`, { params })
}
/**
*
* @param params
* @returns
*/
export function getGoodsCategoryList(params: Record<string, any>) {
return request.get(`shop/goods/coupon/init`, { params })
}
/**
*
* @param params
* @returns
*/
export function addCoupon(params: Record<string, any>) {
return request.post(`shop/goods/coupon`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @returns
*/
export function getCouponStatusList() {
return request.get(`shop/goods/coupon/status`)
}
/**
*
* @param params
* @returns
*/
export function getCouponList(params: Record<string, any>) {
return request.get(`shop/goods/coupon`, { params })
}
/**
*
* @param params
* @returns
*/
export function getCouponSelectList(params: Record<string, any>) {
return request.get(`shop/goods/coupon/select`, { params })
}
/**
*
* @param params
* @returns
*/
export function getSelectedCouponList(params: Record<string, any>) {
return request.get(`shop/goods/coupon/selected`, { params })
}
/**
*
* @param params
* @returns
*/
export function getCouponRecords(params: Record<string, any>) {
return request.get(`shop/goods/coupon/records`, { params });
}
/**
*
* @param id
* @returns
*/
export function getCouponInfo(id: number) {
return request.get(`shop/goods/coupon/detail/${ id }`);
}
/**
*
* @param params
* @returns
*/
export function editCouponStatus(params: Record<string, any>) {
return request.put(`shop/goods/coupon/setstatus/${ params.status }`, params, { showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function editCoupon(params: Record<string, any>) {
return request.put(`shop/goods/coupon/edit/${ params.id }`, params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param params
* @returns
*/
export function deleteCoupon(params: Record<string, any>) {
return request.post(`shop/goods/coupon/delete`, params, { showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function closeCoupon(params: Record<string, any>) {
return request.put(`shop/goods/coupon/invalid`, params, { showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function sendCoupon(params: Record<string, any>) {
return request.post(`shop/goods/coupon/send/${ params.id }`, params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param params
* @returns
*/
export function getCouponSendRecords(params: Record<string, any>) {
return request.get(`shop/goods/coupon/send/pages/${ params.id }`, { params });
}
/**
* init
* @returns
*/
export function initCoupon() {
return request.get(`shop/goods/coupon/send/init`)
}
/************ 限时折扣 ****************/
/**
*
* @param params
* @returns
*/
export function getActiveDiscountPageList(params: Record<string, any>) {
return request.get(`shop/active/discount`, { params })
}
/**
*
* @returns
*/
export function getActiveDiscountStatusList() {
return request.get(`shop/active/status`)
}
/**
*
*
* @param active_id
* @returns
*/
export function getBasicInformation(active_id: number) {
return request.get(`shop/active/discount/info/${ active_id }`);
}
/**
*
* @param active_id
* @returns
*/
export function getActiveDiscountInfo(active_id: number) {
return request.get(`shop/active/discount/${ active_id }`);
}
/**
*
* @param params
* @returns
*/
export function addActiveDiscount(params: Record<string, any>) {
return request.post('shop/active/discount', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function editActiveDiscount(params: Record<string, any>) {
return request.put(`shop/active/discount/${ params.discount_id }`, params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param active_id
* @returns
*/
export function closeActiveDiscount(active_id: number) {
return request.put(`shop/active/discount/close/${ active_id }`, {}, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param active_id
* @returns
*/
export function deleteActiveDiscount(active_id: number) {
return request.delete(`shop/active/discount/${ active_id }`, { showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function batchCloseActiveDiscount(params: Record<string, any>) {
return request.post(`shop/active/discount/batchClose`, params, { showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function batchDeleteActiveDiscount(params: Record<string, any>) {
return request.post(`shop/active/discount/batchDelete`, params, { showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function getActiveDiscountGoodsPageList(params: Record<string, any>) {
return request.get(`shop/active/discount/goods/${ params.active_id }`, { params })
}
/**
*
* @param params
* @returns
*/
export function getActiveDiscountOrderPageList(params: Record<string, any>) {
return request.get(`shop/active/discount/order/${ params.active_id }`, { params })
}
/**
*
* @param params
* @returns
*/
export function goodscheck(params: Record<string, any>) {
return request.post(`shop/active/discount/goods/check`, params, {})
}
/**
*
* @param params
* @returns
*/
export function getActiveDiscountMemberPageList(params: Record<string, any>) {
return request.get(`shop/active/discount/member/${ params.active_id }`, { params })
}
/**
*
* @returns
*/
export function getActiveDiscountConfig() {
return request.get(`shop/active/discount/config`);
}
/**
*
* @param params
* @returns
*/
export function editActiveDiscountConfig(params: Record<string, any>) {
return request.put(`shop/active/discount/config`, params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/********** 积分商品 ***********/
/**
*
* @param params
* @returns
*/
export function getActiveExchangePageList(params: Record<string, any>) {
return request.get(`shop/active/exchange`, { params })
}
/**
*
* @param params
* @returns
*/
export function getActiveExchangeSelectPageList(params: Record<string, any>) {
return request.get(`shop/active/exchange/select`, { params })
}
/**
*
* @param id id
* @returns
*/
export function getActiveExchangeInfo(id: number) {
return request.get(`shop/active/exchange/${ id }`);
}
/**
*
* @param params
* @returns
*/
export function addActiveExchange(params: Record<string, any>) {
return request.post('shop/active/exchange', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function editActiveExchange(params: Record<string, any>) {
return request.put(`shop/active/exchange/${ params.id }`, params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param params
* @returns
*/
export function editActiveExchangeStatus(params: Record<string, any>) {
return request.put(`shop/active/exchange/status/${ params.id }`, params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param id
* @returns
*/
export function deleteActiveExchange(id: number) {
return request.delete(`shop/active/exchange/${ id }`, { showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function batchDeleteActiveExchange(params: Record<string, any>) {
return request.post(`shop/active/exchange/batchDelete`, params, { showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function batchDownActiveExchange(params: Record<string, any>) {
return request.post(`shop/active/exchange/batchDown`, params, { showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function batchUpActiveExchange(params: Record<string, any>) {
return request.post(`shop/active/exchange/batchUp`, params, { showSuccessMessage: true })
}
/**
*
* @returns
*/
export function getActiveExchangeStatus() {
return request.get(`shop/active/exchange/status`)
}
/************ 新人专享 ****************/
/**
*
* @returns
*/
export function getActiveNewcomerConfig() {
return request.get(`shop/active/newcomer/config`);
}
/**
*
* @param params
* @returns
*/
export function editActiveNewcomerConfig(params: Record<string, any>) {
return request.put(`shop/active/newcomer/config`, params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
* -
* @param params
* @returns
*/
export function getNewcomerGoodsList(params: Record<string, any>) {
return request.get('shop/active/newcomer/goods/select', { params })
}
/**
* -
* @param params
* @returns
*/
export function getNewcomerSelectGoodsList(params: Record<string, any>) {
return request.get('shop/active/newcomer/goods/selectgoodssku', { params })
}
/************ 商品榜单 ****************/
/**
*
* @param params
* @returns
*/
export function setRankConfig(params: Record<string, any>) {
return request.post('shop/good/rank/config', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @returns
*/
export function getRankConfig() {
return request.get(`shop/good/rank/config`)
}
/**
*
* @param params
* @returns
*/
export function getRankPageList(params: Record<string, any>) {
return request.get(`shop/good/rank`, { params })
}
/**
*
* @returns
*/
export function optionData() {
return request.get(`shop/good/rank/dict`)
}
/**
*
* @param rank_id
* @returns
*/
export function getRankInfo(rank_id: number) {
return request.get(`shop/good/rank/${ rank_id }`);
}
/**
*
* @param params
* @returns
*/
export function addGoodRank(params: Record<string, any>) {
return request.post('shop/good/rank', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function editGoodRank(params: Record<string, any>) {
return request.put(`shop/good/rank/${ params.id }`, params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param params
* @returns
*/
export function editRankStatus(params: Record<string, any>) {
return request.put(`shop/goods/rank/status`, params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param id
* @returns
*/
export function deleteGoodRank(id: number) {
return request.delete(`shop/good/rank/${ id }`, { showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function batchDelete(params: Record<string, any>) {
return request.put(`shop/good/rank/batchDelete`, params, { showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function modifyGoodsRankSort(params: Record<string, any>) {
return request.put(`shop/good/rank/sort`, params, { showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function getSelectRankPageList(params: Record<string, any>) {
return request.get(`shop/good/rank/select`, { params })
}
/************ 满减 ****************/
/**
*
* @param params
* @returns
*/
export function getManjianList(params: Record<string, any>) {
return request.get(`shop/manjian`, { params })
}
/**
*
* @returns
*/
export function getManjianStatusList() {
return request.get(`shop/manjian/status`)
}
/**
*
* @param params
* @returns
*/
export function addManjian(params: Record<string, any>) {
return request.post('shop/manjian', params)
}
/**
*
* @param params
* @returns
*/
export function editManjian(params: Record<string, any>) {
return request.put(`shop/manjian/${ params.id }`, params)
}
/**
*
* @param params
* @returns
*/
export function getManjianInfo(params: Record<string, any>) {
return request.get(`shop/manjian/init`, { params });
}
/**
*
* @param params
* @returns
*/
export function goodsCheck(params: Record<string, any>) {
return request.post('shop/manjian/goods/check', params)
}
/**
*
* @param params
* @returns
*/
export function getManjianMemberPageList(params: Record<string, any>) {
return request.get(`shop/manjian/member/${ params.id }`, { params })
}
/**
*
* @param manjian_id
* @returns
*/
export function closeManjian(manjian_id: number) {
return request.put(`shop/manjian/close/${ manjian_id }`, {}, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
* ,使
* @param params
* @returns
*/
export function getGoodsSelectByReplaceBuy(params: Record<string, any>) {
return request.get(`shop/goods/buy/goods/select`, { params })
}
/**
* ,使
* @param params
* @returns
*/
export function getGoodsSelectedByReplaceBuy(params: Record<string, any>) {
return request.get(`shop/goods/buy/goods/selected`, { params })
}
/**
*
* @param params
* @returns
*/
export function getGoodsSkuInfo(params: Record<string, any>) {
return request.get(`shop/goods/buy/sku/select`, { params })
}
/*
*
* @param manjian_id
* @returns
*/
export function deleteManjian(manjian_id: number) {
return request.delete(`shop/manjian/${ manjian_id }`, { showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function batchDeleteManjian(params: Record<string, any>) {
return request.put(`shop/manjian/goods/batchDelete`, params, { showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function batchCloseMajian(params: Record<string, any>) {
return request.put(`shop/manjian/goods/batchClose`, params, { showSuccessMessage: true })
}

View File

@ -0,0 +1,281 @@
import request from '@/utils/request'
/**
*
* @returns
*/
export function getConfig() {
return request.get('shop/order/config')
}
/**
*
* @returns
*/
export function setConfig(param: any) {
return request.post('shop/order/config', param, { showSuccessMessage: true })
}
/**
*
* @returns
*/
export function getOrderList(params: Record<string, any>) {
return request.get('shop/order/list', { params })
}
/**
*
* @returns
*/
export function getOrderDetail(order_id: number) {
return request.get(`shop/order/detail/${ order_id }`)
}
/**
*
* @return
*/
export function getOrderStatus() {
return request.get(`shop/order/status`)
}
/**
*
* @return
*/
export function getOrderType() {
return request.get(`shop/order/type`)
}
/**
*
* @return
*/
export function orderClose(order_id: number) {
return request.put(`shop/order/close/${ order_id }`)
}
/**
*
* @return
*/
export function orderDelete(params: Record<string, any>) {
return request.post(`shop/order/delete`,params, { showSuccessMessage: true })
}
/**
*
* @return
*/
export function getOrderDeliveryType(params: Record<string, any>) {
return request.get(`shop/order/delivery_type`, { params })
}
/**
*
* @return
*/
export function orderDelivery(params: Record<string, any>) {
return request.put(`shop/order/delivery`, params)
}
/**
*
* @return
*/
export function setShopRemark(params: Record<string, any>) {
return request.put(`shop/order/shop_remark`, params)
}
/**
*
* @return
*/
export function orderFinish(order_id: number) {
return request.put(`shop/order/finish/${ order_id }`)
}
/**
*
* @return
*/
export function deliveryPackage(params: Record<string, any>) {
return request.get(`shop/order/delivery/package`, { params })
}
/**
*
* @return
*/
export function deliveryPackageList(params: Record<string, any>) {
return request.get(`shop/order/delivery/package/list`, { params })
}
/**
* 退
* @param {Record<string, any>} params
* @return
*/
export function orderRefund(params: Record<string, any>) {
return request.get(`shop/order/refund`, { params })
}
/**
* 退
*/
export function orderRefundDetail(refund_id: number) {
return request.get(`shop/order/refund/${ refund_id }`)
}
/**
* 退
* @return
*/
export function auditRefund(params: Record<string, any>) {
return request.put(`shop/order/refund/audit/${ params.order_refund_no }`, params)
}
/**
* 退
* @return
*/
export function refundDelivery(params: Record<string, any>) {
return request.put(`shop/order/refund/delivery/${ params.order_refund_no }`, params)
}
/**
* 退
*/
export function getRefundMoney(params: Record<string, any>) {
return request.get(`shop/order/refund/refund_money`, { params })
}
/**
* 退
*/
export function shopActiveRefund(params: Record<string, any>) {
return request.post(`shop/order/refund/active`, params, { showSuccessMessage: true })
}
/**
*
*/
export function getInvoiceList(params: Record<string, any>) {
return request.get(`shop/invoice`, { params })
}
/**
*
*/
export function getInvoiceDetail(id: number) {
return request.get(`shop/invoice/${ id }`)
}
/**
*
*/
export function setInvoice(id: number, params: Record<string, any>) {
return request.put(`shop/invoice/${ id }`, params, { showSuccessMessage: true })
}
/**
*
*/
export function createInvoice(params: Record<string, any>) {
return request.post(`shop/invoice/add`, params, { showSuccessMessage: true })
}
/**
*
*/
export function auditInvoice(params: Record<string, any>) {
return request.post(`shop/invoice/audit`, params, { showSuccessMessage: true })
}
/**
*
*/
export function getOrderPayType() {
return request.get(`shop/order/pay/type`)
}
/**
*
*/
export function getOrderFrom() {
return request.get(`shop/order/from`)
}
/**
*
* @return
*/
export function orderEditPrice(params: Record<string, any>) {
return request.put(`shop/order/edit_price`, params, { showSuccessMessage: true })
}
/**
*
* @return
*/
export function getOrderEditAddress(params: Record<string, any>) {
return request.get(`shop/order/edit_delivery`, { params })
}
/**
*
* @return
*/
export function getDeliveryList() {
return request.get(`shop/delivery/store/list`)
}
/**
*
* @return
*/
export function orderEditAddress(params: Record<string, any>) {
return request.put(`shop/order/edit_delivery`, params)
}
/**
*
* @return
*/
export function getOrderBatchDeliveryList(params: Record<string, any>) {
return request.get(`shop/order_batch_delivery`, { params })
}
/**
*
* @return
*/
export function addBatchOrderDelivery(params: Record<string, any>) {
return request.put(`shop/order_batch_delivery/add_batch_order_delivery`, params)
}
/**
*
* @return
*/
export function getOrderBatchDeliveryState() {
return request.get(`shop/order_batch_delivery/get_status`)
}
/**
*
* @return
*/
export function getOrderBatchDeliveryType() {
return request.get(`shop/order_batch_delivery/get_type`)
}
/**
*
* @return
*/
export function closeRefund(order_refund_no: number) {
return request.put(`shop/order/refund/close/${order_refund_no}`)
}

View File

@ -0,0 +1,43 @@
import request from '@/utils/request'
/**
*
*/
export function getShopCountList() {
return request.get(`shop/stat/total`)
}
/**
*
*/
export function getShopTodayCountList() {
return request.get(`shop/stat/today`)
}
/**
*
*/
export function getShopYesterdayCountList() {
return request.get(`shop/stat/yesterday`)
}
/**
*
*/
export function getShopStat() {
return request.get(`shop/stat`)
}
/**
*
*/
export function getShopOrderStat() {
return request.get(`shop/stat/order`)
}
/**
*
*/
export function getShopGoodsStat() {
return request.get(`shop/stat/goods`)
}

View File

@ -0,0 +1,62 @@
import request from '@/utils/request'
/**
*
* @param params
* @returns
*/
export function getShopAddressList(params: Record<string, any>) {
return request.get(`shop/shop_address`, { params })
}
/**
*
* @param id id
* @returns
*/
export function getShopAddressInfo(id: number) {
return request.get(`shop/shop_address/${ id }`);
}
/**
*
* @param params
* @returns
*/
export function addShopAddress(params: Record<string, any>) {
return request.post('shop/shop_address', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function editShopAddress(params: Record<string, any>) {
return request.put(`shop/shop_address/${ params.id }`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @returns
*/
export function deleteShopAddress(id: number) {
return request.delete(`shop/shop_address/${ id }`, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @returns
*/
export function getShopDefaultDeliveryAddressInfo() {
return request.get('shop/shop_address/default/delivery');
}
/**
*
* @returns
*/
export function getOrderRefundAddress() {
return request.get('shop/order/refund/address');
}

View File

@ -0,0 +1,32 @@
import request from '@/utils/request'
/**
*
* @returns
*/
export function getGoodsStatisticsBasic(params: Record<string, any>) {
return request.get('shop/goods/statistics/basic', { params })
}
/**
*
* @returns
*/
export function getGoodsStatisticsTrend(params: Record<string, any>) {
return request.get('shop/goods/statistics/trend', { params })
}
/**
*
*/
export function getGoodsStatisticsType() {
return request.get(`shop/goods/statistics/type`)
}
/**
*
* @returns
*/
export function getGoodsStatisticsRank(params: Record<string, any>) {
return request.get('shop/goods/statistics/rank', { params })
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@ -0,0 +1,35 @@
{
"contactName": "联系人",
"mobile": "联系方式",
"provinceId": "省",
"cityId": "市",
"districtId": "区",
"address": "详细地址",
"fullAddress": "地址",
"lat": "纬度",
"lng": "经度",
"isDeliveryAddress": "是否是发货地址",
"isRefundAddress": "是否是退货地址",
"isDefaultDelivery": "默认发货地址",
"isDefaultRefund": "默认收货地址",
"contactNamePlaceholder": "请输入联系人",
"mobilePlaceholder": "请输入联系方式",
"mobileTips": "请输入正确的手机号",
"addressPlaceholder": "请输入详细地址",
"fullAddressPlaceholder": "请输入地址",
"latPlaceholder": "请输入纬度",
"lngPlaceholder": "请输入经度",
"isDeliveryAddressPlaceholder": "请输入是否是发货地址",
"isRefundAddressPlaceholder": "请输入是否是退货地址",
"isDefaultDeliveryPlaceholder": "请输入默认发货地址",
"isDefaultRefundPlaceholder": "请输入默认收货地址",
"addShopAddress": "添加商家地址库",
"updateShopAddress": "编辑商家地址库",
"shopAddressDeleteTips": "确定要删除该商家地址库吗?",
"addressType": "地址类型",
"deliveryAddress": "发货地址",
"refundAddress": "收货地址",
"defaultDeliveryAddress": "是否设为默认发货地址",
"defaultRefundAddress": "是否设为默认收货地址",
"addressTypeRequire": "至少需设置一项类型"
}

View File

@ -0,0 +1,25 @@
{
"id": "",
"contactName": "联系人",
"mobile": "联系方式",
"mobilePlaceholder": "请输入联系方式",
"provinceId": "省",
"cityId": "市",
"districtId": "区",
"address": "详细地址",
"fullAddress": "地址",
"fullAddressPlaceholder": "请输入地址",
"lat": "纬度",
"lng": "经度",
"isDeliveryAddress": "是否是发货地址",
"isRefundAddress": "是否是退货地址",
"isDefaultDelivery": "默认发货地址",
"isDefaultRefund": "默认收货地址",
"addShopAddress": "添加商家地址库",
"updateShopAddress": "编辑商家地址库",
"shopAddressDeleteTips": "确定要删除该数据吗?",
"addressType": "地址类型",
"deliveryAddress": "发货地址",
"refundAddress": "收货地址",
"default": "默认"
}

View File

@ -0,0 +1,150 @@
{
"goodsSelectPopupSelectGoodsButton": "选择商品",
"rankSelectPopupSelectRankButton": "选择榜单",
"goodsSelectPopupSelect": "已选",
"goodsSelectPopupPiece": "个",
"goodsSelectPopupSelectGoodsDialog": "商品选择",
"goodsSelectPopupAllGoods": "全部商品",
"defaultGoodsSelect": "默认",
"goodsSelectPopupSelectedGoods": "已选商品",
"goodsSelectPopupGoodsName": "商品名称",
"goodsSelectPopupGoodsNamePlaceholder": "请输入商品名称",
"goodsSelectPopupGoodsStatusPlaceholder": "请选择商品状态",
"goodsSelectPopupGoodsCategory": "商品分类",
"goodsSelectPopupGoodsCategoryPlaceholder": "全部",
"goodsSelectPopupGoodsType": "商品类型",
"goodsSelectPopupGoodsTypePlaceholder": "请选择商品类型",
"goodsSelectPopupGoodsInfo": "商品",
"goodsSelectPopupGoodsPointUnit": "积分",
"goodsSelectPopupGoodsPriceUnit": "元",
"goodsSelectPopupPrice": "价格",
"goodsSelectPopupStock": "库存",
"goodsSelectPopupBeforeTip": "已选择",
"goodsSelectPopupAfterTip": "个商品",
"goodsSelectPopupClearGoods": "取消选择",
"goodsSelectPopupGoodsMinTip": "所选商品数量不能少于",
"goodsSelectPopupGoodsMaxTip": "所选商品数量不能超过",
"goodsTextColor": "文字颜色",
"textColor": "标题颜色",
"subTextColor": "副标题颜色",
"wayPlaceholder": "虚拟商品和实物商品只能选择其中一个类型,请重新选择",
"goodsStyle": "商品样式",
"goodsBgColor": "商品背景",
"goodsNameColor": "商品名称",
"goodsPriceColor": "销售价",
"goodsBtnText": "文本",
"goodsBtnTextPlaceholder": "请输入按钮文字",
"goodsSaleColor": "商品销量",
"goodsBtnColor": "按钮背景",
"goodsNumColor": "已兑人数",
"goodsBuyBtn": "购买按钮",
"goodsShowContent": "显示内容",
"goodsBtnResult": "按钮效果",
"goodsBtnIsShow": "是否显示",
"goodsCartIncident": "点击事件",
"goodsAddCart": "加入购物车",
"goodsBtnStyle": "样式",
"goodsSaleNum": "商品销量",
"goodsDetail": "商品详情",
"goodsRounded": "圆角",
"fenxiaoCommissionPriceColor": "分销佣金",
"goodsCategoryPlaceholder": "请选择商品分类",
"goodsPlaceholder": "请选择商品",
"goodsIsBold": "是否加粗",
"goodsLabel": "商品标签",
"headStyle1": "样式1",
"headStyle2": "样式2",
"headStyle3": "样式3",
"headStyle4": "样式4",
"goodsData": "商品数据",
"sortWay": "排序",
"default": "综合",
"sales": "销量",
"price": "价格",
"manyGoodsListAroundRadius": "图片圆角",
"manyGoodsListCategoryName": "分类名称",
"manyGoodsListSubTitle": "副标题",
"manyGoodsLisAddItem": "添加一个多商品组",
"couponData": "优惠券数据",
"couponContent": "优惠券内容",
"couponTitle": "标题",
"couponTitlePlaceholder": "请输入标题",
"couponSubTitle": "副标题",
"couponSubTitlePlaceholder": "请输入副标题",
"selectCoupon": "选择优惠券",
"allSources": "全部",
"couponNum": "优惠券数量",
"couponPlaceholder": "请选择优惠券",
"couponBtnText": "按钮文字",
"couponBtnTextPlaceholder": "请输入按钮文字",
"couponTitleStyle": "标题样式",
"couponTitleColor": "标题颜色",
"couponSubTitleColor": "副标题颜色",
"couponItemStyle": "优惠券项样式",
"couponMoney": "金额颜色",
"manyGoodsListCategorySet": "分组设置",
"manyGoodsListSourceDiy": "自定义",
"manyGoodsListSourceCategory": "商品分类",
"goodsSet": "商品设置",
"goodsSearchSet": "搜索设置",
"goodsSearchText": "搜索内容",
"goodsSearchTextPlaceholder": "请输入搜索内容",
"shopMemberInfoComponentUidTextColor": "编号颜色",
"shopMemberInfoComponentAccountTextColor": "账户颜色",
"shopMemberInfoComponentAccount": "账号信息",
"shopGoodsRecommendComponentTag": "标签",
"shopGoodsRecommendComponentTagColor": "标签颜色",
"shopGoodsRecommendComponentTagPlaceholder": "请输入标签内容",
"shopGoodsRecommendComponentButtonBorderColor": "按钮边框颜色",
"carouselStyle": "轮播样式",
"recommendIndicatorStyle": "指示器设置",
"recommendIndicatorColor": "常规颜色",
"recommendIndicatorActiveColor": "选中颜色",
"activeCubeBlockContent": "板块内容",
"bgImage": "背景图片",
"rankingTitleIcon": "图标",
"rankSelect": "榜单选择",
"rankName": "榜单名称",
"rankNamePlaceholder": "请输入榜单名称",
"showGoodsNum": "榜单商品数量",
"goodsSourceName": "商品来源",
"ruleTypeName": "排序规则",
"rankTypeName": "排行周期",
"rankSelectPopupAfterTip": "个榜单",
"rankSelectPopupGoodsMinTip": "所选榜单数量不能少于",
"rankSelectPopupGoodsMaxTip": "所选榜单数量不能超过",
"rankingTitleImage": "头部图片",
"rankingSubTitle": "副标题",
"rankingSubTitleTextColor": "副标题颜色",
"rankTextColor": "名称颜色",
"rankingSubTitleLink": "副标题链接",
"listFrameColor": "背景颜色",
"topRounded": "上圆角",
"bottomRounded": "下圆角",
"imageRounded": "图片圆角",
"rankingStyle": "板块样式",
"styleRecommend": "风格推荐",
"countDownStyle": "倒计时样式",
"newcomerNumberColor": "数字颜色",
"newcomerNumberBg": "数字背景色",
"newcomerOtherColor": "文字颜色",
"goodsCategorySelectContentName": "分类名称",
"goodsCategorySelectContentImage": "分类图片",
"goodsCategorySelectContentPlaceholder": "请选择商品分类",
"couponSelectContentTitle": "优惠券名称",
"couponSelectContentTitlePlaceholder": "请输入优惠券名称",
"couponSelectContentType": "类型",
"couponSelectContentPrice": "面值",
"couponSelectContentThreshold": "使用门槛",
"couponSelectContentTips": "请选择优惠券",
"pointExchangeSelectContentTitle": "商品名称",
"pointExchangeSelectContentTitlePlaceholder": "请输入商品名称",
"pointExchangeSelectContentGoodsInfo": "商品信息",
"pointExchangeSelectContentPrice": "兑换价格",
"pointExchangeSelectContentPointUnit": "积分",
"pointExchangeSelectContentPriceUnit": "元",
"pointExchangeSelectContentRedeemedAndSurplus": "已兑/剩余",
"pointExchangeSelectContentTips": "请选择积分商品",
"categoryName": "分类名称",
"categoryImage": "分类图片"
}

View File

@ -0,0 +1,20 @@
{
"companyId": "",
"companyIdPlaceholder": "请输入",
"companyName": "名称",
"companyNamePlaceholder": "请输入物流公司名称",
"logo": "LOGO",
"logoPlaceholder": "请输入物流公司logo",
"url": "网址",
"urlPlaceholder": "请输入物流公司网址",
"expressNoPlaceholder": "请输入物流公司编号",
"expressNoTips": "物流公司编号作用于物流查询,请根据物流跟踪对应配置设置编号",
"expressNoKd100": "快递100物流跟踪编号",
"expressNoKd100Placeholder": "请输入快递100物流跟踪编号",
"addCompany": "添加物流公司",
"updateCompany": "编辑物流公司",
"electronicSheetSwitchName": "是否支持电子面单",
"expressNoElectronicSheet": "快递鸟电子面单编号",
"expressNo": "快递鸟物流跟踪编号",
"companyDeleteTips": "确定要删除该数据吗?"
}

View File

@ -0,0 +1,44 @@
{
"companyId": "",
"companyIdPlaceholder": "请输入",
"companyName": "名称",
"companyNamePlaceholder": "请输入物流公司名称",
"logo": "LOGO",
"logoPlaceholder": "请输入物流公司logo",
"url": "网址",
"urlPlaceholder": "请输入物流公司网址",
"expressNo": "快递鸟物流跟踪编号",
"expressNoPlaceholder": "请输入快递鸟物流公司编号",
"expressNoTips": "物流公司编号作用于物流查询,请根据物流跟踪对应配置设置编号",
"expressNoKd100": "快递100物流跟踪编号",
"expressNoKd100Placeholder": "请输入快递100物流跟踪编号",
"addCompany": "添加物流公司",
"updateCompany": "编辑物流公司",
"expressNoElectronicSheet": "快递鸟电子面单编号",
"expressNoElectronicSheetPlaceholder": "请输入快递鸟电子面单编号",
"expressNoElectronicSheetTips": "电子面单编号作用于电子面单查询,请根据电子面单对应配置设置编号",
"printStyle": "模板样式",
"addPrintStyle": "添加模板样式",
"expType": "业务类型",
"addExpType": "添加业务类型",
"expTypeName": "业务名称",
"expTypeTextTips": "业务名称不能为空",
"expTypeValueTips": "业务值不能为空",
"expTypeTextRepeatTips": "业务名称不可以重复,请重新填写",
"expTypeValueRepeatTips": "业务值不可以重复,请重新填写",
"expTypeValueNullTips": "业务值不可以为零",
"expTypeValue": "业务值",
"electronicSheetSwitch": "是否支持电子面单",
"printStyleName": "模板名称",
"printStyleNameTips": "模板名称不能为空",
"printStyleSizeTips": "模板尺寸不能为空",
"printStyleNameRepeatTips": "模板名称不可以重复,请重新填写",
"printStyleSizeRepeatTips": "模板尺寸不可以重复,请重新填写",
"printStyleId": "模板尺寸",
"expTypeTips": "快递鸟业务类型",
"expTypeTips1": "不填默认为1",
"printStyleTips": "快递鸟模版规格(常用)",
"printStyleTips1": "主流快递单打印纸尺寸一般为76*130100*180单位mm",
"printStyleTips2": "不填写则取物流公司的默认模板",
"examine": "点击查看"
}

View File

@ -0,0 +1,11 @@
{
"store": "启动门店自提后,买家可选择自提点提货。",
"express": "启用物流配送后,买家下单可以选择快递发货。",
"local_delivery": "启用同城配送后,在配送范围内的买家可以选择同城配送。",
"deliveryStaff": "配送员",
"deliveryCompany": "物流公司",
"deliveryTemplate": "运费模版",
"deliverySearch": "物流跟踪",
"deliveryStore": "自提点",
"localConfig": "同城配送设置"
}

View File

@ -0,0 +1,17 @@
{
"tabESTemplate": "电子面单模板",
"tabESConfig": "设置",
"templateName": "模板名称",
"templateNamePlaceholder": "请输入模板名称",
"expressCompany": "物流公司",
"expressCompanyPlaceholder": "请选择物流公司",
"status": "状态",
"statusOn": "开启",
"statusOff": "关闭",
"addElectronicSheet": "添加电子面单",
"payType": "邮费支付方式",
"isDefault": "默认",
"setDefault": "设为默认",
"electronicSheetDeleteTips": "确定要删除该数据吗?",
"electronicSheetSetDefaultTips": "确定要设置为默认模版吗?"
}

View File

@ -0,0 +1,21 @@
{
"tabESTemplate": "电子面单模板",
"tabESConfig": "设置",
"apiSet": "接口设置",
"interfaceType": "接口类型",
"prompt": "提示",
"promptTips1-1": "请到快递鸟官网申请",
"kdn": "快递鸟",
"kdnEBusinessIDLabel": "用户ID",
"kdnEBusinessIDPlaceholder": "请输入快递鸟用户ID",
"kdnEBusinessIDTips": "快递鸟用户IDEBusinessID",
"kdnAppKeyPlaceholder": "请输入快递鸟API key",
"kdnAppKeyTips": "快递鸟分配的API key",
"printerSet": "打印机设置",
"serverPort1": "服务器端口1",
"serverPort1Placeholder": "请输入服务器端口1",
"serverPort2": "服务器端口2",
"serverPort2Placeholder": "请输入服务器端口2",
"httpsPort": "HTTPS端口",
"httpsPortPlaceholder": "请输入HTTPS端口"
}

View File

@ -0,0 +1,34 @@
{
"basicSettings": "基础设置",
"otherSettings": "其他设置",
"templateName": "模板名称",
"templateNamePlaceholder": "请输入模板名称",
"expressCompany": "物流公司",
"expressCompanyPlaceholder": "请选择物流公司",
"expType": "业务类型",
"status": "状态",
"customerName": "CustomerName",
"customerNamePlaceholder": "请输入电子面单客户账号",
"customerPwd": "CustomerPwd",
"customerPwdTips": "电子面单密码,不同快递叫法不同,例如:密码、密钥、客户编号",
"sendSite": "SendStaff",
"sendSiteTips": "不同快递叫法不同,例如:网点编码、网点编号、网点名称",
"sendStaff": "SendStaff",
"sendStaffTips": "不同快递叫法不同,例如:收件快递员、取件员编号、网点名称",
"monthCode": "MonthCode",
"monthCodeTips": "不同快递叫法不同,例如:月结号、密钥、月结编码",
"payType": "邮费支付方式",
"isNotice": "快递员上门揽件",
"yes": "是",
"no": "否",
"isNoticeTips": "是否通知快递员上门揽件,跨越速运,京东快运,电商标快,填舱标快,填舱电标等业务类型必填",
"printStyle": "模板样式",
"printStylePlaceholder": "请选择模板样式",
"printStyleTips": "快递鸟模版规格(常用)",
"printStyleTips1": "主流快递单打印纸尺寸一般为76*130100*180单位mm",
"printStyleTips2": "不填写则取物流公司的默认模板",
"isDefault": "是否默认",
"examine": "点击查看",
"customerNameTips": "快递鸟电子面单账号申请",
"customerNameTips1": "电子面单账号对照表"
}

View File

@ -0,0 +1,91 @@
{
"basicSettings": "同城配送基础设置",
"timeIsOpen": "配送时间设置",
"timeIsOpenTips": "开启后,买家下单选择同城配送时,可选择配送时间,提交订单后,将在买家备注中显示。关闭后,买家下单默认为立即配送",
"close": "关闭",
"open": "开启",
"everyDay": "每天",
"monday": "周一",
"tuesday": "周二",
"wednesday": "周三",
"thursday": "周四",
"friday": "周五",
"saturday": "周六",
"sunday": "周日",
"timeWeekRequire": "请选择配送时间",
"deliveryTimeSetting": "配送时间设置",
"feeType": "收费标准",
"region": "按区域收取配送费",
"distance": "按距离收取配送费",
"district": "按行政区域收取配送费",
"feeSetting": "费用设置",
"weightFee": "续重收费",
"feeSettingTextOne": "km内按",
"feeSettingTextTwo": "元收取配送费,每超出",
"feeSettingTextThree": "km费用增加",
"priceUnit": "元",
"weightFeeTextOne": "商品重量",
"weightFeeTextTwo": "kg 内不额外收费,每超出",
"weightFeeTextThree": "kg 费用增加",
"areaName": "区域名称",
"startPrice": "起送价",
"deliveryPrice": "配送费",
"areaType": "划分方式",
"radius": "半径",
"custom": "自定义",
"addDeliveryArea": "添加配送区域",
"baseDistRequire": "请输入起始公里数",
"gradDistRequire": "请输入超出公里数",
"basePriceRequire": "请输入起始公里内的配送费用",
"gradPriceRequire": "请输入每超出公里部分的费用",
"areaNameRequire": "请输入区域名称",
"startPriceRequire": "请输入起送价",
"startPriceMin": "起送价不能小于0",
"deliveryPriceRequire": "请输入配送费",
"deliveryPriceMin": "配送费不能小于0",
"areaPlaceholder": "请添加配送区域",
"deliveryType": "配送方式",
"business": "商家自配送",
"deliveryTypeRequire": "至少需选择一种配送方式",
"deliveryAddress": "取货地址",
"defaultDeliveryAddressEmpty": "请先配置默认发货地址",
"toSetting": "去配置",
"update": "修改",
"deliveryAddressChange": "取货地址已变更请注意是否需重新调整配送区域",
"tradeTimePlaceholderTwo": "请选择自提时段",
"tradeTimePlaceholderThree": "请选择细分时段",
"tradeTimePlaceholderFour": "结束时间不能小于或等于开始时间",
"tradeTimePlaceholderFive": "后一个时间段的开始时间不能小于前一个时间段的结束时间",
"thrid":"三方配送",
"AppKey":"AppKey",
"AppSecret":"AppSecret",
"shopId":"商户ID",
"shopStoreNo":"商户门店编号",
"AppKeyRequire":"请输入AppKey",
"AppSecretRequire":"请输入AppSecret",
"shopIdRequire":"请输入商户ID",
"shopStoreNoRequire":"请输入商户门店编号",
"deliveryTime":"配送时段设置",
"startTime":"开始时间",
"endTime":"结束时间",
"timeInterval":"细分时段",
"addTime":"添加配送时段",
"deliveryTimeTips":"配送时段设置,若不在配送时段内,则无法进行配送",
"30minute": "30分钟",
"90minute": "90分钟",
"oneHour": "一小时",
"twoHour": "两小时",
"advancaDay": "提前预约",
"advance": "提前",
"day": "天",
"reservationAvailable":"可预约",
"withinDays":"天内",
"advanceTips": "若设置了提前预约,则用户在当前日期前多少天可以预约配送,不可以立即配送",
"mostDays": "最长预约",
"mostDaysTips": "预约配送最长可预约多少天内进行提货",
"formatError": "格式不正确",
"notLessThanZero": "不能小于0",
"mustBeGreaterThanZero": "必须大于0",
"thridRequire": "请选择一个三方配送服务",
"thridSeting": "请填写完整的三方配送配置"
}

View File

@ -0,0 +1,21 @@
{
"prompt": "提示",
"promptTips1-1": "请到快递鸟官网申请",
"promptTips1-2": "1、按单付费查询相同单号查询多次计费一次代码8001",
"promptTips1-3": "2、按次付费查询相同单号查询多次计费多次代码8002",
"promptTips2": "请到快递100官网申请",
"interfaceType": "接口类型",
"kdn": "快递鸟",
"kd100": "快递100",
"kdnEBusinessIDPlaceholder": "请输入快递鸟EBusinessID",
"kdnEBusinessIDTips": "快递鸟电商ID",
"kdnAppKeyPlaceholder": "请输入快递鸟AppKey",
"kdnAppKeyTips": "快递鸟分配的电商加密私钥",
"isPayEdition": "套餐类型",
"PayPerUse": "按次付费",
"PayPerOrder": "按单付费",
"kd100AppKeyPlaceholder": "请输入快递100AppKey",
"kd100AppKeyTips": "快递100应用密钥",
"kd100CustomerPlaceholder": "请输入快递100Customer",
"kd100CustomerTips": "快递100分配给的公司编号"
}

View File

@ -0,0 +1,9 @@
{
"addDeliveryPersonnel": "添加配送员",
"updateDeliver": "编辑配送员",
"deliverName": "配送员名称",
"deliverMobile": "配送员手机号",
"deliverNamePlaceholder": "请输入配送员名称",
"deliverMobilePlaceholder": "请输入配送员手机号",
"deliverDeleteTips": "确定要删除该数据吗?"
}

View File

@ -0,0 +1,18 @@
{
"storeId": "",
"storeName": "自提点信息",
"storeNamePlaceholder": "请输入自提点名称",
"storeLogo": "自提点logo",
"storeMobile": "联系电话",
"address": "详细地址",
"fullAddress": "联系地址",
"longitude": "经度",
"latitude": "纬度",
"tradeTime": "营业时间",
"createTime": "添加时间",
"createTimePlaceholder": "请输入添加时间",
"addStore": "添加自提点",
"updateStore": "编辑自提点",
"storeDeleteTips": "确定要删除该数据吗?",
"storeInfo": "自提点信息"
}

View File

@ -0,0 +1,60 @@
{
"storeName": "自提点名称",
"storeDesc": "简介",
"storeLogo": "自提点logo",
"storeMobile": "手机号",
"provinceId": "省id",
"cityId": "市",
"districtId": "县(区)",
"address": "详细地址",
"fullAddress": "完整地址",
"longitude": "经度",
"latitude": "纬度",
"tradeTime": "营业时间",
"createTime": "下单时间",
"updateTime": "更新时间",
"storeNamePlaceholder": "请输入自提点名称",
"storeDescPlaceholder": "请输入简介",
"storeLogoPlaceholder": "请上传自提点logo",
"storeMobilePlaceholder": "请输入手机号",
"provinceIdPlaceholder": "请选择省id",
"cityIdPlaceholder": "请选择市",
"districtIdPlaceholder": "请选择县(区)",
"addressPlaceholder": "请输入详细地址",
"fullAddressPlaceholder": "请输入完整地址",
"longitudePlaceholder": "请输入经度",
"latitudePlaceholder": "请输入纬度",
"tradeTimePlaceholder": "请输入营业时间",
"tradeTimeTips": "例上午9:00-12:00下午2:00-6:00",
"createTimePlaceholder": "请选择添加时间",
"updateTimePlaceholder": "请输入更新时间",
"addStore": "添加自提点",
"updateStore": "编辑自提点",
"storeDeleteTips": "确定要删除该自提点吗?",
"storeAddress": "自提点地址",
"storeAddressPlaceholder": "请选择自提点地址",
"storeAddressDetail": "自提点详细地址",
"storeAddressDetailPlaceholder": "请输入自提点详细地址",
"storeTime": "自提时段",
"storeDate": "自提日期",
"addTimeRange": "添加时段",
"storeTimeInterval": "细分时段",
"monday": "周一",
"tuesday": "周二",
"wednesday": "周三",
"thursday": "周四",
"friday": "周五",
"saturday": "周六",
"sunday": "周日",
"minute": "分钟",
"hour": "小时",
"startTime": "开始时间",
"endTime": "结束时间",
"tradeTimePlaceholderTwo": "请选择自提时段",
"selectBusinessDays": "请选择自提日期",
"tradeTimePlaceholderThree": "请选择细分时段",
"tradeTimePlaceholderFour": "结束时间不能小于或等于开始时间",
"tradeTimePlaceholderFive": "后一个时间段的开始时间不能小于前一个时间段的结束时间",
"storeDateTips": "所设时间段为可自提时间段,可通过“细分时段”对该自提时段进一步切分",
"storeTimeIntervalTips": "对上方的“自提时段”进行切分,在手机端上将显示切换后的时段供用户选择"
}

View File

@ -0,0 +1,12 @@
{
"templateName": "运费模板名称",
"templateNamePlaceholder": "请输入运费模板名称",
"createTime": "创建时间",
"addTemplate": "添加运费模板",
"updateTemplate": "编辑运费模板",
"templateDeleteTips": "确定要删除该模板吗?",
"feeTypeName": "计费类型",
"freeShipping": "指定区域包邮",
"open": "启用",
"close": "关闭"
}

View File

@ -0,0 +1,39 @@
{
"templateName": "运费模板名称",
"templateNamePlaceholder": "请输入运费模板名称",
"createTime": "创建时间",
"addTemplate": "添加运费模板",
"updateTemplate": "编辑运费模板",
"templateDeleteTips": "确定要删除该模板吗?",
"feeTypeName": "计费类型",
"isDefault": "默认模板",
"num": "按件",
"weight": "按重量",
"volume": "按体积",
"feeSetting": "费用设置",
"selectArea": "选择地区",
"firstNum": "首件(件)",
"continueNum": "续件(件)",
"firstWeight": "首件重量kg",
"fee": "运费(元)",
"continueFee": "续费(元)",
"continueWeight": "续件重量kg",
"firstVolume": "首件体积",
"continueVolume": "续件体积",
"deliveryArea": "配送区域",
"addDeliveryArea": "添加单独配送区域",
"freeShipping": "指定区域包邮",
"freeShippingArea": "包邮区域",
"addFreeShippingArea": "添加包邮区域",
"freeShippingAreaTips": "两个条件满足其一便可以包邮",
"noDelivery": "不配送区域",
"addNoDelivery": "添加不配送区域",
"freeShippingNum": "包邮件数",
"freeShippingWeight": "包邮重量kg",
"freeShippingVolume": "包邮体积",
"freeShippingPrice": "包邮金额",
"areaPlaceholder": "请选择地区",
"noDeliveryPlaceholder": "请选择不配送的地区",
"freeShippingPlaceholder": "请选择包邮的地区",
"notUnderZero": "不能小于等于0"
}

View File

@ -0,0 +1,10 @@
{
"attrName": "参数模板名称",
"attrNamePlaceholder": "请输入参数模板名称",
"sortPlaceholder": "请输入排序号",
"addShopGoodsAttr": "添加参数模板",
"updateShopGoodsAttr": "编辑参数模板",
"goodsAttrDeleteTips": "确定要删除该数据吗?",
"sortTips": "排序号格式输入错误",
"manage": "管理"
}

View File

@ -0,0 +1,18 @@
{
"attrName": "参数模板名称",
"attrNamePlaceholder": "请输入参数名称",
"updateAttr": "编辑参数模板",
"sort": "排序号",
"sortPlaceholder": "请输入排序号",
"addShopGoodsAttr": "添加商品参数",
"attrValueType": "参数类型",
"attrValueTypeRadio": "单选",
"attrValueTypeCheckbox": "多选",
"attrValueTypeText": "输入",
"attrValueChild": "参数内容",
"updateShopGoodsAttr": "编辑商品参数",
"goodsAttrDeleteTips": "确定要删除该数据吗?",
"attrValueName": "参数名称",
"addAttrValue": "添加参数",
"attrValueNamePlaceholder": "请输入参数名称"
}

View File

@ -0,0 +1,19 @@
{
"brandId": "品牌ID",
"brandIdPlaceholder": "请输入品牌ID",
"brandName": "品牌名称",
"brandNamePlaceholder": "请输入品牌名称",
"logo": "品牌logo",
"logoPlaceholder": "请输入品牌logo",
"desc": "品牌介绍",
"descPlaceholder": "请输入品牌介绍",
"sort": "排序",
"sortPlaceholder": "请输入排序",
"addBrand": "添加商品品牌",
"updateBrand": "编辑商品品牌",
"brandDeleteTips": "确定要删除该数据吗?",
"textColor": "文字颜色",
"bgColor": "背景颜色",
"borderColor": "边框颜色",
"colorTips": "若未设置颜色,则为默认色"
}

View File

@ -0,0 +1,26 @@
{
"categoryId": "商品分类id",
"categoryIdPlaceholder": "请输入商品分类id",
"categoryName": "分类名称",
"categoryNamePlaceholder": "请输入分类名称",
"image": "分类图片",
"imagePlaceholder": "请输入分类图片",
"pid": "上级分类",
"pidPlaceholder": "请选择上级分类",
"categoryFullName": "组装分类名称",
"categoryFullNamePlaceholder": "请输入组装分类名称",
"isShow": "是否显示",
"sort": "排序",
"sortPlaceholder": "请输入排序号",
"addCategory": "添加商品分类",
"updateCategory": "编辑商品分类",
"categoryDeleteTips": "确定要删除该数据吗?",
"categoryDeleteTips1": "子级分类也会删除,确定要删除该数据吗?",
"spreadGoodsCategory": "推广",
"goodsCategorySpreadTitle": "商品分类推广",
"spreadLink": "推广链接",
"copy": "复制",
"downloadQrcode": "下载二维码",
"tabGoodsCategory": "商品分类",
"tabGoodsCategoryConfig": "分类设置"
}

View File

@ -0,0 +1,27 @@
{
"categoryTemplate": "分类模板",
"categoryType": "分类类型",
"categorystyleOne": "一级分类",
"categorystyleTwo": "二级分类",
"pageSettings": "页面设置",
"pageTitle": "页面名称",
"pageTitlePlaceholder": "请输入页面名称",
"searchControl": "搜索栏",
"searchTitle": "搜索栏文字",
"searchTitlePlaceholder": "请输入搜索栏文字",
"open": "开启",
"close": "关闭",
"goodsStyle": "商品排列",
"singleCols": "单列",
"doubleCols": "双列",
"sort": "商品排序",
"sortPlaceholder": "请选择商品排序",
"cartControl": "购物车显示",
"cartStyle": "购物车样式",
"cartTextPlaceholder": "请输入购物车按钮文字",
"cartEvent": "点击按钮",
"detail": "跳转商品详情",
"cart": "加入购物车",
"tabGoodsCategory": "商品分类",
"tabGoodsCategoryConfig": "分类设置"
}

View File

@ -0,0 +1,59 @@
{
"evaluateId": "",
"evaluateIdPlaceholder": "请输入",
"orderId": "订单id",
"orderIdPlaceholder": "请输入订单id",
"orderGoodsId": "订单项ID",
"orderGoodsIdPlaceholder": "请输入订单项ID",
"goodsId": "商品ID",
"goodsIdPlaceholder": "请选择商品",
"memberId": "会员ID",
"memberIdPlaceholder": "请输入会员ID",
"content": "评价内容",
"contentPlaceholder": "请输入评价内容",
"images": "评价图片",
"imagesPlaceholder": "请输入评价图片",
"isAnonymous": "是否匿名",
"anonymous": "匿名",
"notAnonymous": "不匿名",
"scores": "评价等级",
"scoresPlaceholder": "请输入评价分数 1-5",
"auditName": "审核状态",
"explainFirst": "商家回复",
"explainFirstPlaceholder": "请输入商家回复",
"createTime": "评价时间",
"createTimePlaceholder": "请输入评价时间",
"again": "追评",
"againTime": "追评时间",
"againTimePlaceholder": "请输入追评时间",
"isShow": "是否显示",
"show": "显示",
"notShow": "不显示",
"isShowPlaceholder": "请输入是否显示 1显示 2不显示",
"addEvaluate": "添加自评",
"updateEvaluate": "编辑商品评价",
"evaluateDeleteTips": "确定要删除该数据吗?",
"goodsInfo": "商品信息",
"memberHead": "会员头像",
"memberName": "会员名称",
"memberNamePlaceholder": "请输入会员名称",
"adopt": "通过",
"refuse": "拒绝",
"auditAdoptTips": "确定要通过审核吗?",
"reply": "回复",
"goodsName": "商品名称",
"goodsNamePlaceholder": "请输入商品名称",
"topping": "置顶",
"cancelTopping": "取消置顶",
"memberHeadPlaceholder": "请上传会员头像",
"batchDelete": "批量删除",
"batchDeleteTips": "确定要批量删除选中的评价吗?",
"batchAdopt": "批量通过",
"batchAdoptTips": "确定要批量通过选中的评价吗?",
"batchRefuse": "批量拒绝",
"batchRefuseTips": "确定要批量拒绝选中的评价吗?",
"batchEmptySelectedCommentsTips": "请先选择要操作的评价",
"status":"审核状态",
"statusPlaceholder":"请选择审核状态"
}

View File

@ -0,0 +1,37 @@
{
"orderId": "订单id",
"orderGoodsId": "订单项ID",
"goodsId": "商品ID",
"memberId": "会员ID",
"content": "评价内容",
"images": "评价图片",
"isAnonymous": "0 匿名 1不匿名",
"scores": "评价分数 1-5",
"isAudit": "审核状态 1待审 2通过 3拒绝",
"explainFirst": "解释内容",
"againContent": "追评内容",
"againImages": "追评图片",
"againExplain": "追评解释",
"againTime": "追评时间",
"againIsAudit": "审核状态 1待审 2通过 3拒绝",
"isShow": "是否显示 1显示 2不显示",
"orderIdPlaceholder": "请输入订单id",
"orderGoodsIdPlaceholder": "请输入订单项ID",
"goodsIdPlaceholder": "请输入商品ID",
"memberIdPlaceholder": "请输入会员ID",
"contentPlaceholder": "请输入评价内容",
"imagesPlaceholder": "请上传评价图片",
"isAnonymousPlaceholder": "请输入0 匿名 1不匿名",
"scoresPlaceholder": "请输入评价分数 1-5",
"isAuditPlaceholder": "请输入审核状态 1待审 2通过 3拒绝",
"explainFirstPlaceholder": "请输入解释内容",
"againContentPlaceholder": "请输入追评内容",
"againImagesPlaceholder": "请上传追评图片",
"againExplainPlaceholder": "请输入追评解释",
"againTimePlaceholder": "请输入追评时间",
"againIsAuditPlaceholder": "请输入审核状态 1待审 2通过 3拒绝",
"isShowPlaceholder": "请输入是否显示 1显示 2不显示",
"addEvaluate": "添加商品评价",
"updateEvaluate": "编辑商品评价",
"evaluateDeleteTips": "确定要删除该商品评价吗?"
}

View File

@ -0,0 +1,12 @@
{
"tabGoodsLabel": "商品标签",
"tabGoodsLabelGroup": "标签分组",
"groupName": "分组名称",
"groupNamePlaceholder": "请输入分组名称",
"sort": "排序",
"sortPlaceholder": "请输入排序",
"addLabelGroup": "添加标签分组",
"updateLabelGroup": "编辑标签分组",
"sortTips": "排序号格式输入错误",
"labelGroupDeleteTips": "确定要删除该数据吗?"
}

View File

@ -0,0 +1,32 @@
{
"tabGoodsLabel": "商品标签",
"tabGoodsLabelGroup": "标签分组",
"labelName": "标签名称",
"labelNamePlaceholder": "请输入标签名称",
"groupName": "标签分组",
"groupNamePlaceholder": "请选择标签分组",
"styleType": "效果设置",
"styleByDiy": "自定义",
"styleByIcon": "图片",
"textColor": "文字颜色",
"bgColor": "背景颜色",
"borderColor": "边框颜色",
"colorTips": "若未设置颜色,则为默认色",
"icon": "上传图片",
"iconTips": "建议尺寸80px*30px若未上传将显示默认",
"memo": "标签说明",
"memoPlaceholder": "请输入标签说明",
"sort": "排序",
"sortPlaceholder": "请输入排序",
"addLabel": "添加商品标签",
"updateLabel": "编辑商品标签",
"sortTips": "排序号格式输入错误",
"labelDeleteTips": "确定要删除该数据吗?",
"statusOn": "开启",
"statusOff": "关闭",
"status": "状态",
"label": "标签",
"createTime": "创建时间",
"copyLabel": "复制",
"labelCopyTips": "确定要复制该标签吗?"
}

View File

@ -0,0 +1,139 @@
{
"goodsName": "商品名称",
"goodsNamePlaceholder": "请输入商品名称",
"goodsCategory": "商品分类",
"goodsCategoryPlaceholder": "全部",
"goodsType": "商品类型",
"goodsTypePlaceholder": "请选择商品类型",
"brand": "商品品牌",
"brandPlaceholder": "请输入商品品牌",
"labelIds": "商品标签",
"labelIdsPlaceholder": "请选择商品标签",
"saleNum": "销量",
"startSaleNumPlaceholder": "最低销量",
"startSaleNumTips": "最低销量输入错误",
"endSaleNumPlaceholder": "最高销量",
"endSaleNumTips": "最高销量输入错误",
"shopSaleNumTips": "最低销量不能大于最高销量",
"skuPrice": "价格",
"startPricePlaceholder": "最低价格",
"startPriceTips": "最低价格输入错误",
"endPricePlaceholder": "最高价格",
"endPriceTips": "最高价格输入错误",
"shopPriceTips": "最低价格不能大于最高价格",
"statusOn": "销售中",
"statusOff": "仓库中",
"statusAll": "全部商品",
"batchOnGoods": "批量上架",
"batchOffGoods": "批量下架",
"batchDeleteGoods": "批量删除",
"batchSetting": "批量设置",
"batchEmptySelectedGoodsTips": "请选择要操作的商品",
"goodsInfo": "商品",
"stock": "库存",
"status": "状态",
"sort": "排序",
"sortTips": "排序号格式输入错误",
"createTime": "创建时间",
"addGoods": "添加商品",
"statusActionOn": "上架",
"statusActionOff": "下架",
"statusChangeTips": "确定要下架该商品吗?",
"spreadGoods": "推广",
"copyGoods": "复制",
"updateGoods": "编辑商品",
"goodsCopyTips": "确定要复制该商品吗?",
"goodsDeleteTips": "确定要删除该商品吗?",
"batchGoodsDeleteTips": "确定要删除这些商品吗",
"batchOperationSku": "批量设置",
"editStockPopupTitle": "编辑商品库存",
"editPricePopupTitle": "编辑商品价格",
"skuName": "规格名称",
"price": "销售价",
"pricePlaceholder": "请输入销售价",
"priceTips": "销售价格式输入错误",
"priceNotZeroTips": "销售价不能小于0",
"marketPrice": "划线价",
"marketPricePlaceholder": "请输入划线价",
"marketPriceTips": "划线价格式输入错误",
"marketPriceNotZeroTips": "划线价不能小于0",
"market_price": "划线价",
"market_pricePlaceholder": "请输入划线价",
"market_priceTips": "划线价格式输入错误",
"market_priceNotZeroTips": "划线价不能小于0",
"costPrice": "成本价",
"costPricePlaceholder": "请输入成本价",
"costPriceTips": "成本价格式输入错误",
"costPriceNotZeroTips": "成本价不能小于0",
"cost_price": "成本价",
"cost_pricePlaceholder": "请输入成本价",
"cost_priceTips": "成本价格式输入错误",
"cost_priceNotZeroTips": "成本价不能小于0",
"stockPlaceholder": "请输入库存",
"stockTips": "库存格式输入错误",
"stockNotZeroTips": "库存不能小于0",
"goodsSpreadTitle": "商品推广",
"spreadLink": "推广链接",
"copy": "复制",
"downloadQrcode": "下载二维码",
"memberPrice": "会员价",
"editMemberPricePopupTitle": "编辑会员价",
"memberDiscount": "会员价优惠方式",
"discount": "会员折扣",
"fixedPrice": "指定会员价",
"nonparticipation": "不参与",
"goodsSku": "商品规格",
"memberEnjoyDiscount": "会员享受折扣",
"discountUnit": "折",
"yuanUnit": "元",
"editMemberPrice": "修改会员价",
"memberLevel": "会员等级",
"memberPricePlaceholder": "请输入会员价",
"discountHint": "会员折扣说明:按照默认会员等级折扣优惠",
"fixedPriceHint": "会员价说明:指定优惠价格,商品未参与活动时,按照会员价优惠,若商品参与活动,则以活动价为准",
"addGoodsLabel": "添加商品标签",
"addGoodsService": "添加商品服务",
"addGoodsCategory": "添加分类",
"addGoodsBrand": "添加品牌",
"addGoodsPoster": "添加海报",
"virtualSaleNumPlaceholder": "请输入虚拟销量",
"posterPlaceholder": "请选择商品海报",
"goodsCategoryPlaceholderTwo": "请选择商品分类",
"virtualSaleNumDesc": "虚拟销量只在前台展示中参与计算",
"virtualSaleNumTips": "虚拟销量格式输入错误",
"virtualSaleNumNotZeroTips": "虚拟销量不能小于0",
"giftTips": "当商品设置为赠品时,该商品仅用于活动赠送,不会在前台展示或出售",
"yes": "是",
"no": "否",
"isFreeShipping": "是否免邮",
"feeType": "运费设置",
"selectTemplate": "选择模板",
"fixedShipping": "统一运费",
"deliveryMoney": "固定运费",
"deliveryMoneyPlaceholder": "请输入固定运费",
"deliveryMoneyTips": "固定运费格式输入错误",
"deliveryMoneyNotZeroTips": "固定运费不能小于0",
"deliveryTemplateId": "运费模板",
"deliveryTemplateIdPlaceholder": "请选择运费模板",
"addDeliveryTemplateId": "添加运费模板",
"deliveryType": "配送方式",
"deliveryTypePlaceholder": "请选择配送方式",
"yuan": "元",
"label": "商品标签",
"service": "商品服务",
"virtualSaleNum": "虚拟销量",
"goodsBrand": "商品品牌",
"goodsPoster": "商品海报",
"isGift": "是否赠品",
"batchSettingTip": "每次仅能设置一项,点击确认成功之后生效",
"setStock": "修改库存",
"addStock": "增加库存",
"reduceStock": "减少库存",
"stockNum": "目标数值",
"stockNumTips": "批量调整商品库存,若商品正在参与营销活动,则不会修改库存",
"confirm": "确认",
"diyForm": "万能表单",
"diyFormPlaceholder": "请选择万能表单",
"addDiyForm": "添加表单",
"productClassificationPlaceholder":"请选择商品分类"
}

View File

@ -0,0 +1,171 @@
{
"addGoods": "添加商品",
"updateGoods": "编辑商品",
"basicInfoTab": "基础信息",
"goodsType": "商品类型",
"goodsName": "商品名称",
"goodsNamePlaceholder": "请输入商品名称",
"goodsNameMaxLengthTips": "商品名称不能超过60个字符",
"subTitle": "副标题",
"subTitlePlaceholder": "请输入副标题",
"subTitleMaxLengthTips": "副标题不能超过100个字符",
"goodsImage": "商品图片",
"goodsImagePlaceholder": "请上传商品图片",
"goodsVideo": "商品视频",
"goodsVideoPlaceholder": "请上传商品视频",
"goodsVideoTipTile": "注意事项:",
"goodsVideoTipOne": "1、检查upload文件夹是否有读写权限。",
"goodsVideoTipTwo": "2、PHP默认上传限制为2MB需要在php.ini配置文件中修改“post_max_size”和“upload_max_filesize”的大小。",
"goodsVideoTipThree": "3、视频支持手动输入外链视频地址或者上传本地视频文件",
"goodsVideoTipFour": "4、必须上传.mp4视频格式",
"goodsVideoTipFive": "5、视频文件大小不能超过500MB",
"goodsCategory": "商品分类",
"refresh": "刷新",
"addGoodsCategory": "添加分类",
"goodsCategoryPlaceholder": "请选择商品分类",
"brand": "商品品牌",
"brandPlaceholder": "请输入商品品牌",
"addGoodsBrand": "添加品牌",
"poster": "商品海报",
"posterPlaceholder": "请选择商品海报",
"addGoodsPoster": "添加海报",
"posterTips": "不设置将使用默认海报",
"diyForm": "万能表单",
"diyFormPlaceholder": "请选择万能表单",
"addDiyForm": "添加表单",
"label": "商品标签",
"addGoodsLabel": "添加商品标签",
"goodsService": "商品服务",
"addGoodsService": "添加商品服务",
"supplier": "供应商",
"supplierPlaceholder": "请选择供应商",
"addSupplier": "添加供应商",
"status": "商品状态",
"statusOn": "上架",
"statusOff": "下架",
"isGive": "是否赠品",
"yes": "是",
"no": "否",
"sort": "排序",
"sortPlaceholder": "请输入排序",
"sortTips": "排序号格式输入错误",
"priceStockTab": "价格库存",
"specType": "规格类型",
"singleSpec": "单规格",
"multiSpec": "多规格",
"price": "销售价",
"marketPrice": "划线价",
"costPrice": "成本价",
"weight": "重量",
"volume": "体积",
"goodsStock": "商品库存",
"goodsStockPlaceholder": "请输入商品库存",
"skuNo": "商品编码",
"yuan": "元",
"defaultUnit": "件",
"skuNoPlaceholder": "请输入商品编码",
"goodsSku": "商品规格",
"specNamePlaceholder": "请输入规格项,如颜色、尺码、大小",
"specValueNamePlaceholder": "请输入规格值,如:白色",
"addSpecValue": "+添加规格值",
"addSpec": "添加规格",
"batchOperationSku": "批量设置",
"all": "全部",
"stock": "库存",
"skuWeight": "重量(kg)",
"skuVolume": "体积(m³)",
"confirm": "确定",
"image": "图片",
"defaultSku": "默认规格",
"unit": "单位",
"unitPlaceholder": "请输入单位,默认为:件",
"virtualSaleNum": "虚拟销量",
"virtualSaleNumPlaceholder": "请输入虚拟销量",
"virtualSaleNumDesc": "虚拟销量只在前台展示中参与计算",
"virtualSaleNumTips": "虚拟销量格式输入错误",
"virtualSaleNumNotZeroTips": "虚拟销量不能小于0",
"giftTips": "当商品设置为赠品时,该商品仅用于活动赠送,不会在前台展示或出售",
"maxAddSpecTips": "最多添加5个规格项",
"pleaseEditSpecPlaceholder": "请编辑规格信息",
"refreshSuccess": "刷新成功",
"deliveryTab": "配送设置",
"advancedSetup": "高级设置",
"diyDetailTemplate": "商品详情模版",
"diyDetailTemplatePlaceholder": "请选择商品详情模版",
"addDetailTemp": "添加模板",
"detailTempTip": "不设置将使用默认商品详情模板",
"deliveryType": "配送方式",
"deliveryTypePlaceholder": "请选择配送方式",
"pleaseSelectSku": "请先选择商品规格",
"isLimit": "是否限购",
"isLimitTips": "启用限购后,购买商品时,会对该商品购买量做限制判断。",
"limitType": "限购类型",
"limitTypeTips": "单次限购是针对于每次下单不能超过限购数量,单人限购是针对于会员账号购买这个商品的总数不能超过限购数量。",
"singleTime": "单次限购",
"singlePerson": "单人限购",
"maxBuy": "限购数量",
"maxBuyPlaceholder": "请输入限购数量",
"maxBuyTips": "[限购数量]格式输入错误",
"maxBuyWarnTips": "限购数量超出商品库存时,买家将无法购买该商品",
"maxBuyNotZeroTips": "限购数量不能小于1",
"minBuy": "起购数量",
"minBuyTips": "起购数量超出商品库存时,买家将无法购买该商品",
"minBuyFormatErrorTips": "[起购数量]格式输入错误",
"minBuyNotZeroTips": "起购数量不能小于0",
"minBuyGreaterThanMaxBuyTips": "起购数量不能大于限购数量",
"isFreeShipping": "是否免邮",
"feeType": "运费设置",
"selectTemplate": "选择模板",
"fixedShipping": "统一运费",
"deliveryMoney": "固定运费",
"deliveryMoneyPlaceholder": "请输入固定运费",
"deliveryMoneyTips": "固定运费格式输入错误",
"deliveryMoneyNotZeroTips": "固定运费不能小于0",
"deliveryTemplateId": "运费模板",
"deliveryTemplateIdPlaceholder": "请选择运费模板",
"addDeliveryTemplateId": "添加运费模板",
"goodsDesc": "商品详情",
"goodsDescPlaceholder": "请填写商品详情",
"goodsDescMaxTips": "商品描述字符数应在550000之间",
"pricePlaceholder": "请输入销售价",
"priceTips": "[销售价]格式输入错误",
"priceNotZeroTips": "销售价不能小于0",
"marketPricePlaceholder": "请输入划线价",
"marketPriceTips": "[划线价]格式输入错误",
"marketPriceNotZeroTips": "划线价不能小于0",
"costPricePlaceholder": "请输入成本价",
"costPriceTips": "[成本价]格式输入错误",
"costPriceNotZeroTips": "成本价不能小于0",
"weightPlaceholder": "请输入重量",
"weightTips": "[重量(kg)]格式输入错误",
"weightNotZeroTips": "重量(kg)不能小于0",
"volumePlaceholder": "请输入体积",
"volumeTips": "[体积(m³)]格式输入错误",
"volumeNotZeroTips": "体积(m³)不能小于0",
"stockPlaceholder": "请输入库存",
"stockTips": "[库存]格式输入错误",
"stockNotZeroTips": "库存不能小于0",
"specNameRequire": "规格项不能为空",
"specNameRepeat": "规格项不能重复",
"specValueRequire": "规格值不能为空",
"specValueNameRepeat": "规格值不能重复",
"lackDefaultSpec": "商品缺少默认规格",
"goodsArguments": "商品参数",
"goodsArgumentsTemp": "商品参数模板",
"goodsArgumentsTempPlaceholder": "请选择商品参数模板",
"goodsArgumentsTempHint": "商品可以添加自定义商品参数,也可以通过参数模板批量设置商品参数",
"argumentsName": "参数名",
"argumentsValue": "参数值",
"argumentsSortHint": "设置排序,改变商品规格展示顺序",
"operation": "操作",
"delAttr": "删除",
"noData": "无数据",
"addGoodsArguments": "添加商品参数",
"memberDiscount": "会员等级折扣",
"discount": "会员折扣",
"fixedPrice": "指定会员价",
"nonparticipation": "不参与",
"discountHint": "会员折扣说明:按照默认会员等级折扣优惠",
"fixedPriceHint": "会员价说明:指定优惠价格,商品未参与活动时,按照会员价优惠,若商品参与活动,则以活动价为准",
"participateInActiveDisableTips": "商品正在参与营销活动,禁止操作"
}

View File

@ -0,0 +1,22 @@
{
"goodsName": "商品名称",
"goodsNamePlaceholder": "请输入商品名称",
"goodsCategory": "商品分类",
"goodsCategoryPlaceholder": "全部",
"goodsType": "商品类型",
"goodsTypePlaceholder": "请选择商品类型",
"price": "价格",
"saleNum": "销量",
"statusOn": "销售中",
"statusOff": "仓库中",
"batchRecycle": "批量恢复",
"batchEmptySelectedGoodsTips": "请选择要操作的商品",
"goodsInfo": "商品",
"stock": "库存",
"stockPlaceholder": "请输入商品库存(总和)",
"status": "状态",
"createTime": "创建时间",
"recycle": "恢复",
"goodsRecycleTips": "确定要恢复该商品吗?",
"batchGoodsRecycleTips": "确定要恢复这些商品吗"
}

View File

@ -0,0 +1,26 @@
{
"goodsSearch": "商品搜索",
"goodsCode": "商品编码",
"defaultSearch": "默认搜索",
"defaultWord": "默认搜索关键词",
"defaultWordPlaceholder": "请输入默认搜索关键词",
"defaultWordTips": "默认搜索,将显示在前台搜索框,前台点击时直接作为关键词进行搜索",
"hotSearch": "热门搜索",
"indexKeyword": "搜索的关键字",
"searchPlaceholder": "请输入搜索关键字",
"addSearch": "添加",
"enable": "商品编码唯一性",
"enableTips": "开启后,将验证商品编码唯一性,不能重复",
"searchTips": "列表中已存在该关键字",
"keyWordTips": "搜索关键字不能为空",
"goodSort": "商品排序",
"sortType": "排序方式",
"sortTypePlaceholder": "请选择排序方式",
"sortTypeTips": "选择排序方式时,前台商品将依据排序号优先进行正序或倒序排列,当排序号相同时,均按照创建时间倒序排列。",
"sortColumn": "排序字段",
"sortColumnTips": "请选择用于排序的字段,例如 排序号、价格、销量,不同的排序字段将影响商品的显示优先级。",
"sortColumnPlaceholder": "请选择排序字段",
"defaultSort": "默认排序值",
"defaultSortPlaceholder": "请输入默认排序值",
"defaultSortTips": "默认排值是当添加商品时,如果没有设置排序值的情况下,则按照该位置设置的默认数值参与排序 "
}

View File

@ -0,0 +1,10 @@
{
"serviceName": "服务名称",
"serviceNamePlaceholder": "请输入服务名称",
"desc": "描述",
"descPlaceholder": "请输入描述",
"addServe": "添加商品服务",
"updateServe": "编辑商品服务",
"serveDeleteTips": "确定要删除该数据吗?",
"imagePlaceholder": "请上传服务图片"
}

View File

@ -0,0 +1,164 @@
{
"addGoods": "添加商品",
"updateGoods": "编辑商品",
"basicInfoTab": "基础信息",
"goodsType": "商品类型",
"goodsName": "商品名称",
"goodsNamePlaceholder": "请输入商品名称",
"goodsNameMaxLengthTips": "商品名称不能超过60个字符",
"subTitle": "副标题",
"subTitlePlaceholder": "请输入副标题",
"subTitleMaxLengthTips": "副标题不能超过100个字符",
"goodsImage": "商品图片",
"goodsImagePlaceholder": "请上传商品图片",
"goodsVideo": "商品视频",
"goodsVideoPlaceholder": "请上传商品视频",
"goodsVideoTipTile": "注意事项:",
"goodsVideoTipOne": "1、检查upload文件夹是否有读写权限。",
"goodsVideoTipTwo": "2、PHP默认上传限制为2MB需要在php.ini配置文件中修改“post_max_size”和“upload_max_filesize”的大小。",
"goodsVideoTipThree": "3、视频支持手动输入外链视频地址或者上传本地视频文件",
"goodsVideoTipFour": "4、必须上传.mp4视频格式",
"goodsVideoTipFive": "5、视频文件大小不能超过500MB",
"goodsCategory": "商品分类",
"advancedSetup": "高级设置",
"diyDetailTemplate": "商品详情模版",
"diyDetailTemplatePlaceholder": "请选择商品详情模版",
"addDetailTemp": "添加模板",
"detailTempTip": "不设置将使用默认商品详情模板",
"refresh": "刷新",
"addGoodsCategory": "添加分类",
"goodsCategoryPlaceholder": "请选择商品分类",
"brand": "商品品牌",
"brandPlaceholder": "请输入商品品牌",
"addGoodsBrand": "添加品牌",
"poster": "商品海报",
"posterPlaceholder": "请选择商品海报",
"addGoodsPoster": "添加海报",
"posterTips": "不设置将使用默认海报",
"diyForm": "万能表单",
"diyFormPlaceholder": "请选择万能表单",
"addDiyForm": "添加表单",
"label": "商品标签",
"addGoodsLabel": "添加商品标签",
"goodsService": "商品服务",
"addGoodsService": "添加商品服务",
"supplier": "供应商",
"supplierPlaceholder": "请选择供应商",
"addSupplier": "添加供应商",
"status": "商品状态",
"statusOn": "上架",
"statusOff": "下架",
"isGive": "是否赠品",
"yes": "是",
"no": "否",
"sort": "排序",
"sortPlaceholder": "请输入排序",
"sortTips": "排序号格式输入错误",
"priceStockTab": "价格库存",
"specType": "规格类型",
"singleSpec": "单规格",
"multiSpec": "多规格",
"price": "销售价",
"marketPrice": "划线价",
"costPrice": "成本价",
"goodsStock": "商品库存",
"goodsStockPlaceholder": "请输入商品库存",
"skuNo": "商品编码",
"yuan": "元",
"defaultUnit": "件",
"skuNoPlaceholder": "请输入商品编码",
"goodsSku": "商品规格",
"specNamePlaceholder": "请输入规格项,如颜色、尺码、大小",
"specValueNamePlaceholder": "请输入规格值,如:白色",
"addSpecValue": "+添加规格值",
"addSpec": "添加规格",
"batchOperationSku": "批量设置",
"all": "全部",
"stock": "库存",
"confirm": "确定",
"image": "图片",
"defaultSku": "默认规格",
"unit": "单位",
"unitPlaceholder": "请输入单位,默认为:件",
"virtualSaleNum": "虚拟销量",
"virtualSaleNumPlaceholder": "请输入虚拟销量",
"virtualSaleNumDesc": "虚拟销量只在前台展示中参与计算",
"virtualSaleNumTips": "虚拟销量格式输入错误",
"virtualSaleNumNotZeroTips": "虚拟销量不能小于0",
"giftTips": "当商品设置为赠品时,该商品仅用于活动赠送,不会在前台展示或出售",
"virtualSetTips": "当设置为店内核销时,若存在未完成的订单,则无法编辑",
"maxAddSpecTips": "最多添加5个规格项",
"pleaseEditSpecPlaceholder": "请编辑规格信息",
"refreshSuccess": "刷新成功",
"isLimit": "是否限购",
"isLimitTips": "启用限购后,购买商品时,会对该商品购买量做限制判断。",
"limitType": "限购类型",
"limitTypeTips": "单次限购是针对于每次下单不能超过限购数量,单人限购是针对于会员账号购买这个商品的总数不能超过限购数量。",
"singleTime": "单次限购",
"singlePerson": "单人限购",
"maxBuy": "限购数量",
"maxBuyPlaceholder": "请输入限购数量",
"maxBuyTips": "[限购数量]格式输入错误",
"maxBuyWarnTips": "限购数量超出商品库存时,买家将无法购买该商品",
"maxBuyNotZeroTips": "限购数量不能小于1",
"minBuy": "起购数量",
"minBuyTips": "起购数量超出商品库存时,买家将无法购买该商品",
"minBuyFormatErrorTips": "[起购数量]格式输入错误",
"minBuyNotZeroTips": "起购数量不能小于0",
"minBuyGreaterThanMaxBuyTips": "起购数量不能大于限购数量",
"goodsDesc": "商品详情",
"goodsDescPlaceholder": "请填写商品详情",
"goodsDescMaxTips": "商品描述字符数应在550000之间",
"pricePlaceholder": "请输入销售价",
"priceTips": "[销售价]格式输入错误",
"priceNotZeroTips": "销售价不能小于0",
"marketPricePlaceholder": "请输入划线价",
"marketPriceTips": "[划线价]格式输入错误",
"marketPriceNotZeroTips": "划线价不能小于0",
"costPricePlaceholder": "请输入成本价",
"costPriceTips": "[成本价]格式输入错误",
"costPriceNotZeroTips": "成本价不能小于0",
"stockPlaceholder": "请输入库存",
"stockTips": "[库存]格式输入错误",
"stockNotZeroTips": "库存不能小于0",
"specNameRequire": "规格项不能为空",
"specNameRepeat": "规格项不能重复",
"specValueRequire": "规格值不能为空",
"specValueNameRepeat": "规格值不能重复",
"lackDefaultSpec": "商品缺少默认规格",
"setDeliverGoods": "发货设置",
"autoDeliverGoods": "自动发货",
"handDeliverGoods": "手动发货",
"setTakeGoods": "收货设置",
"autoTakeGoods": "自动收货",
"handTakeGoods": "买家确认收货",
"verifyTakeGoods": "到店核销",
"virtualIndate": "有效期",
"virtualIndatePlaceholder": "请输入有效期",
"verifyVirtualIndate": "核销有效期",
"verifyVirtualIndateOne": "永久",
"verifyVirtualIndateTwo": "购买后几日有效",
"verifyVirtualIndateThree": "指定过期日期",
"verifyHint": "无论何时购买此商品,到达指定时间后都将过期,无法核销。",
"sky": "天",
"virtualIndateErrorHint": "核销有效期不能小于1天",
"virtualIndateErrorOneHint": "核销有效期不能小于等于当前时间",
"goodsArguments": "商品参数",
"goodsArgumentsTemp": "商品参数模板",
"goodsArgumentsTempPlaceholder": "请选择商品参数模板",
"goodsArgumentsTempHint": "商品可以添加自定义商品参数,也可以通过参数模板批量设置商品参数",
"argumentsName": "参数名",
"argumentsValue": "参数值",
"argumentsSortHint": "设置排序,改变商品规格展示顺序",
"operation": "操作",
"delAttr": "删除",
"noData": "无数据",
"addGoodsArguments": "添加商品参数",
"memberDiscount": "会员等级折扣",
"discount": "会员折扣",
"fixedPrice": "指定会员价",
"nonparticipation": "不参与",
"discountHint": "会员折扣说明:按照默认会员等级折扣优惠",
"fixedPriceHint": "会员价说明:指定优惠价格,商品未参与活动时,按照会员价优惠,若商品参与活动,则以活动价为准",
"participateInActiveDisableTips": "商品正在参与营销活动,禁止操作"
}

View File

@ -0,0 +1,20 @@
{
"realtimeOverview": "实时概况",
"updateTime": "更新时间:",
"todayOrderCount": "今日订单数",
"todayOrderSale": "今日销售额",
"todayAddMemberCount": "今日退款金额",
"todayBrowseCount": "今日浏览量",
"yesterday": "昨日:",
"orderCount": "订单总数",
"salesTotal": "销售总额(元)",
"memberTotal": "退款金额",
"browseTotal": "总浏览量",
"agentMatters": "待办事项",
"waitPayOrder": "待付款订单",
"waitDeliveryOrder": "待发货订单",
"waitTakeOrder": "待收货订单",
"refundOrder": "退款订单",
"saleGoodsNum": "出售商品数量",
"warehouseGoodsNum": "仓库商品数量"
}

View File

@ -0,0 +1,42 @@
{
"addCoupon": "添加优惠券",
"title": "名称",
"titlePlaceholder": "请如XXX优惠券最多20个字",
"type": "类型",
"price": "面值",
"receiveType": "是否手动领取",
"startTime": "开始时间",
"endTime": "结束时间",
"remainCount": "剩余数量",
"limitCount": "已领取数量",
"receive": "领取记录",
"user": "是",
"grant": "否",
"limit": "限量",
"unlimited": "不限量",
"receiveNumber": "发放数量",
"pricePlaceholder": "请输入优惠券面值",
"remainCountPlaceholder": "最多发放100000张",
"reduction": "满减券",
"noThreshold": "无门槛券",
"threshold": "使用门槛",
"minConditionMoneyPlaceholder": "请输入优惠券的最低消费金额",
"userLimitCount": "限领张数",
"userLimitCountPlaceholder": "请输入限领张数",
"receiveTime": "领取时间",
"limitedTime": "限时",
"unlimitedTime": "不限时",
"days": "天数",
"times": "固定时间",
"validType": "有效期",
"lengthPlaceholder": "请输入使用时间",
"status": "状态",
"open": "开启",
"close": "关闭",
"cancel": "取消",
"save": "保存",
"startDate": "开始时间",
"endDate": "结束时间",
"validTimePlaceholder": "用券截止时间",
"arrivalTimePlaceholder": "领券截止时间"
}

View File

@ -0,0 +1,42 @@
{
"editCoupon": "编辑优惠券",
"title": "名称",
"titlePlaceholder": "请如XXX优惠券最多20个字",
"type": "类型",
"price": "面值",
"receiveType": "是否手动领取",
"startTime": "开始时间",
"endTime": "结束时间",
"remainCount": "剩余数量",
"limitCount": "已领取数量",
"receive": "领取记录",
"user": "是",
"grant": "否",
"limit": "限量",
"unlimited": "不限量",
"receiveNumber": "发放数量",
"pricePlaceholder": "请输入优惠券面值",
"remainCountPlaceholder": "最多发放100000张",
"reduction": "满减券",
"noThreshold": "无门槛券",
"threshold": "使用门槛",
"minConditionMoneyPlaceholder": "请输入优惠券的最低消费金额",
"userLimitCount": "限领张数",
"userLimitCountPlaceholder": "请输入限领张数",
"receiveTime": "领取时间",
"limitedTime": "限时",
"unlimitedTime": "不限时",
"days": "天数",
"times": "固定时间",
"validType": "使用时间",
"lengthPlaceholder": "请输入使用时间",
"status": "状态",
"open": "开启",
"close": "关闭",
"cancel": "取消",
"save": "保存",
"startDate": "开始时间",
"endDate": "结束时间",
"validTimePlaceholder": "用券截止时间",
"arrivalTimePlaceholder": "领券截止时间"
}

View File

@ -0,0 +1,83 @@
{
"addCoupon": "添加优惠券",
"title": "名称",
"titlePlaceholder": "请输入优惠券名称",
"type": "类型",
"price": "面值",
"receiveType": "是否手动领取",
"startTime": "开始时间",
"endTime": "结束时间",
"count": "数量",
"countTips1": "1、剩余数量是指可主动领取优惠券的剩余数量总数是指可主动领取优惠券的累计数量",
"countTips2": "2、已领取数量是指用户在前台主动领取的优惠券数量",
"countTips3": "3、已发放数量是指商家在后台给用户直接发放以及用户参与各种营销活动发放的优惠券数量",
"countTips4": "4、发放数量不受优惠券剩余数量限制",
"sumCount": "剩余 / 总数",
"giveCount": "已发放",
"noLimit": "不限量",
"receive": "领取记录",
"edit": "编辑",
"Status": "是否关闭领取",
"threshold": "使用门槛",
"reduction": "满减券",
"noThreshold": "无门槛券",
"couponDeleteTips": "确定要删除该优惠券吗?",
"couponCloseTips": "确定要关闭该优惠券吗?关闭后将不可领取,若会员已领取将变为失效",
"validType": "有效期",
"statusName": "状态",
"receiveTypeTime": "领取有效期",
"spreadGoods": "推广",
"close": "关闭",
"couponSpreadTitle": "优惠券推广",
"downloadQrcode": "下载二维码",
"spreadLink": "推广链接",
"receiveUseCount": "已使用",
"collectionCoupon": "领取记录",
"collectionTitle": "优惠券名称",
"userName": "领用会员",
"collectionReceiveType": "领用方式",
"createTime": "领取时间",
"expireTime": "到期时间",
"status": "当前状态",
"useTime": "使用时间",
"validity": "使用有效期",
"mobile": "手机号",
"memberInfo": "会员信息",
"memberInfoPlaceholder": "请输入会员编码/昵称/手机号搜索",
"collectionSumCount": "发放数量",
"couponInfo": "优惠券基本信息",
"receiveCount": "已领取",
"receiveExpireCount": "已过期",
"receiveInvalidCount": "已失效",
"showOrder": "查看订单",
"send": "发券",
"sendRecord": "发放记录",
"rangeTypePlaceholder": "请选择发放对象",
"sendTime": "发放时间",
"sendStatus": "发放状态",
"selectCoupon": "优惠券名称",
"sendNum": "每人发放数量",
"sendNumPrompt": "请输入每人发放数量",
"num": "张",
"sendNumTip": "不受优惠券库存限制",
"sendNumTipTwo": "输入框限制不能输入超过10",
"rangeType": "发放会员",
"point": "积分",
"balance": "余额",
"memberLabel": "会员标签",
"memberLabelPlaceholder": "请选择会员标签",
"memberLevel": "会员等级",
"memberLevelPlaceholder": "请选择会员等级",
"memberLevelTip": "会员等级不能为空",
"memberTip": "会员不能为空",
"memberLabelTip": "会员标签不能为空",
"sendNumTipThree": "发放数量不能为空",
"sendNumTipFour": "每人发放数量要大于0小于10",
"sendNumTipFive": "输入格式不正确",
"sendCouponTitle": "优惠券发放",
"batchEmptySelectedGoodsTips": "请先选择需要批量操作的优惠券",
"batchDelete": "批量删除",
"batchClose": "批量关闭",
"batchCloseTips": "确定要关闭选中的优惠券吗?关闭后将不可领取,若会员已领取将变为失效",
"batchDeleteTips": "确定要删除选中的优惠券吗?"
}

View File

@ -0,0 +1,23 @@
{
"selectCoupon": "选择的优惠券",
"sendNum": "每人发放数量",
"sendNumPrompt": "请输入每人发放数量",
"num": "张",
"sendNumTip": "不受优惠券库存限制",
"sendNumTipTwo": "输入框限制不能输入超过10",
"rangeType": "发放对象",
"memberInfo": "会员信息",
"mobile": "手机号",
"point": "积分",
"balance": "余额",
"memberLabel": "会员标签",
"memberLabelPlaceholder": "请选择会员标签",
"memberLevel": "会员等级",
"memberLevelPlaceholder": "请选择会员等级",
"memberLevelTip": "会员等级不能为空",
"memberTip": "会员不能为空",
"memberLabelTip": "会员标签不能为空",
"sendNumTipThree": "发放数量不能为空",
"sendNumTipFour": "每人发放数量要大于0小于10",
"sendNumTipFive": "输入格式不正确"
}

View File

@ -0,0 +1,38 @@
{
"editDiscount": "添加限时折扣",
"name": "活动名称",
"namePlaceholder": "请输入活动名称",
"nameTip": "活动名称用于展示在商家后台管理",
"title": "标题",
"titlePlaceholder": "请输入活动标题",
"titleTip": "活动标题用户展示在手机或者电脑前端展示使用建议输入长度不要大于5",
"activityTime": "活动时间",
"discountTimePlaceholder": "请选择活动时间",
"selectProduct": "选择商品",
"selectProductPlaceholder": "请选择商品",
"noSpaceAllowed": "内容不能为空",
"batchOperation": "批量设置",
"batchEmptySelectedGoodsTips": "请选择要操作的商品",
"discountType": "折扣类型",
"price": "原价",
"discounts": "打折",
"discountsTips": "[打折]格式错误",
"discountsTipsTwo": "打折折扣不可小于0",
"discountsTipsThree": "打折折扣不可大于9.9",
"discountsPlaceholder": "请输入打折折扣",
"reduceMoney": "减钱",
"reduceMoneyTips": "[减钱]格式错误",
"reduceMoneyTipsTwo": "减钱金额不可小于0",
"reduceMoneyTipsThree": "减钱金额不可大于原价金额",
"reduceMoneyPlaceholder": "请输入减钱金额",
"promotional": "促销价",
"promotionalTips": "[促销价]格式错误",
"promotionalTipsTwo": "促销价金额不可小于0",
"promotionalTipsThree": "促销价金额不可大于原价金额",
"promotionalPlaceholder": "请输入促销价",
"delete": "删除",
"enabled": "设为参与",
"noEnabled": "设为不参与",
"skuDiscountSettings": "规格折扣设置",
"skuDiscountSettingsPlaceholder": "请输入规格折扣设置"
}

View File

@ -0,0 +1,8 @@
{
"headTitle": "顶部广告图",
"image": "图片上传",
"imagePlaceholder": "请上传图片",
"toLink": "跳转链接",
"toLinkPlaceholder": "请输入跳转链接",
"addConfigList": "添加广告图"
}

View File

@ -0,0 +1,37 @@
{
"baseInfo": "基础信息",
"name": "活动名称",
"title": "标题",
"status": "状态",
"paymentAmount": "支付金额",
"memberCount": "参与会员数",
"orderCount": "支付订单数",
"createTime": "创建时间",
"startTime": "活动开始时间",
"endTime": "活动结束时间",
"keywordPlaceholder": "请输入商品名称",
"keyword": "商品名称",
"goodsInfo": "商品信息",
"price": "商品价格",
"activeOrderMoney": "累计订单金额",
"activeOrderNum": "累计订单数",
"activeMemberNum": "参与会员数",
"activeSuccessNum": "支付销量",
"orderInfo": "订单编号",
"payTime": "支付时间",
"orderNo": "订单编号",
"orderMoney": "订单金额",
"buyInfo": "买家/收货人",
"payType": "支付类型",
"orderStatus": "订单状态",
"toBePaid": "待支付",
"toBeShipped": "待发货",
"shipped": "已发货",
"receivedGoods": "已收货",
"completed": "已完成",
"closed": "已关闭",
"consumptionMoney": "消费总额",
"participationNum": "参与次数",
"orderTime": "最后下单时间",
"memberInfo": "会员信息"
}

View File

@ -0,0 +1,38 @@
{
"editDiscount": "编辑限时折扣",
"name": "活动名称",
"namePlaceholder": "请输入活动名称",
"nameTip": "活动名称用于展示在商家后台管理",
"title": "标题",
"titlePlaceholder": "请输入活动标题",
"titleTip": "活动标题用于在手机或者电脑前端建议输入长度不要大于5",
"activityTime": "活动时间",
"discountTimePlaceholder": "请选择活动时间",
"noSpaceAllowed": "内容不能为空",
"selectProduct": "选择商品",
"selectProductPlaceholder": "请选择商品",
"batchOperation": "批量设置",
"batchEmptySelectedGoodsTips": "请选择要操作的商品",
"discountType": "折扣类型",
"price": "原价",
"discounts": "打折",
"discountsTips": "[打折]格式错误",
"discountsTipsTwo": "打折折扣不可小于0",
"discountsTipsThree": "打折折扣不可大于9.9",
"discountsPlaceholder": "请输入打折折扣",
"reduceMoney": "减钱",
"reduceMoneyTips": "[减钱]格式错误",
"reduceMoneyTipsTwo": "减钱金额不可小于0",
"reduceMoneyTipsThree": "减钱金额不可大于原价金额",
"reduceMoneyPlaceholder": "请输入减钱金额",
"promotional": "促销价",
"promotionalTips": "[促销价]格式错误",
"promotionalTipsTwo": "促销价金额不可小于0",
"promotionalTipsThree": "促销价金额不可大于原价金额",
"promotionalPlaceholder": "请输入促销价",
"delete": "删除",
"enabled": "设为参与",
"noEnabled": "设为不参与",
"skuDiscountSettings": "规格折扣设置",
"skuDiscountSettingsPlaceholder": "请输入规格折扣设置"
}

View File

@ -0,0 +1,54 @@
{
"addDiscount": "添加限时折扣",
"name": "名称",
"namePlaceholder": "请输入活动名称",
"title": "标题",
"status": "状态",
"statusPlaceholder": "请选择状态",
"paymentAmount": "支付金额",
"memberCount": "会员数",
"orderCount": "订单数",
"discountTime": "活动时间",
"detail": "详情",
"close": "关闭",
"closeTips": "活动正在进行中,是否确认关闭当前活动?",
"deleteTips": "是否确认删除当前活动",
"baseInfo": "基础信息",
"createTime": "创建时间",
"startTime": "活动开始时间",
"endTime": "活动结束时间",
"keywordPlaceholder": "请输入商品名称",
"keyword": "商品名称",
"goodsInfo": "商品信息",
"price": "商品价格",
"activeOrderMoney": "累计订单金额",
"activeOrderNum": "累计订单数",
"activeMemberNum": "参与会员数",
"activeSuccessNum": "支付销量",
"orderInfo": "订单编号",
"payTime": "支付时间",
"orderNo": "订单编号",
"orderMoney": "订单金额",
"buyInfo": "买家/收货人",
"payType": "支付类型",
"orderStatus": "订单状态",
"toBePaid": "待支付",
"toBeShipped": "待发货",
"shipped": "已发货",
"receivedGoods": "已收货",
"completed": "已完成",
"closed": "已关闭",
"consumptionMoney": "消费总额",
"participationNum": "参与次数",
"orderTime": "最后下单时间",
"memberInfo": "会员信息",
"activeName": "活动名称",
"participationMemberCount": "参与会员数",
"payOrderCount": "支付订单数",
"startDate": "开始时间",
"endDate": "结束时间",
"batchCloseTips": "是否确认批量关闭当前活动?",
"batchDeleteTips": "是否确认批量删除当前活动?",
"batchClose":"批量关闭",
"batchDelete":"批量删除"
}

View File

@ -0,0 +1,62 @@
{
"addGoods": "添加商品",
"baseInfo": "基础设置",
"goodsType": "商品类型",
"goodsName": "商品名称",
"goodsNamePlaceholder": "请输入商品名称",
"goodsTitle": "副标题",
"goodsTitlePlaceholder": "请输入副标题",
"image": "商品图片",
"imagePlaceholder": "请选择商品图片",
"selectProduct": "选择商品",
"selectGoodsPlaceholder": "请选择商品",
"goodsStatus": "商品状态",
"goodsSelect": "选择商品",
"redemptionSettings": "积分设置",
"batchOperation": "批量设置",
"batchEmptySelectedGoodsTips": "请选择要操作的商品",
"goodsSelectPopupGoodsInfo": "商品信息",
"price": "销售价",
"stock": "兑换库存",
"stockPlaceholder": "请输入兑换库存",
"stockTips": "[兑换库存]格式错误",
"stockTipsTwo": "兑换库存不可小于等于0",
"stockTipsThree": "兑换限制数量不可大于商品库存",
"limit": "兑换限制",
"limitPlaceholder": "请输入兑换限制",
"limitTips": "[兑换限制]格式错误",
"limitTipsTwo": "兑换限制数量不可小于等于0",
"limitTipsThree": "兑换限制数量不能大于兑换库存",
"limitUnit": "件/人",
"integralUnit": "积分",
"prickUnit": "元",
"pointPlaceholder": "请输入积分积分",
"pointTips": "[积分]格式错误",
"pointTipsTwo": "积分不可小于等于0",
"money": "兑换价",
"newPrice": "价格",
"moneyTips": "[价格]格式错误",
"moneyTipsTwo": "价格不可小于0",
"limitRules": "每人每单可兑换件数",
"couponSelect": "选择优惠券",
"couponSelectPlaceholder": "请选择优惠券",
"couponName": "优惠券名称",
"couponType": "类型",
"couponPrice": "面值",
"sumCount": "总库存",
"threshold": "使用门槛",
"validType": "有效期",
"receiveTypeTime": "领取有效期",
"balance": "兑换余额",
"balancePlaceholder": "请输入兑换余额",
"balanceTips": "[兑换余额]格式错误",
"balanceTipsTwo": "兑换余额不可小于等于0",
"balanceType": "余额类型",
"goodsSkuTitle": "选择商品规格",
"goodsSkuPlaceholder": "请选择商品规格",
"goodsStock": "库存",
"enabled": "设为参与",
"noEnabled": "设为不参与",
"noEnabledTip": "请至少设置一个规格参与",
"goodsDetail": "商品详情"
}

View File

@ -0,0 +1,62 @@
{
"editGoods": "编辑商品",
"baseInfo": "基础设置",
"goodsType": "商品类型",
"goodsName": "商品名称",
"goodsNamePlaceholder": "请输入商品名称",
"goodsTitle": "副标题",
"goodsTitlePlaceholder": "请输入副标题",
"image": "商品图片",
"imagePlaceholder": "请选择商品图片",
"selectProduct": "选择商品",
"selectGoodsPlaceholder": "请选择商品",
"goodsStatus": "商品状态",
"goodsSelect": "选择商品",
"redemptionSettings": "积分设置",
"batchOperation": "批量设置",
"batchEmptySelectedGoodsTips": "请选择要操作的商品",
"goodsSelectPopupGoodsInfo": "商品信息",
"price": "销售价",
"stock": "兑换库存",
"stockPlaceholder": "请输入兑换库存",
"stockTips": "[兑换库存]格式错误",
"stockTipsTwo": "兑换库存不可小于等于0",
"stockTipsThree": "兑换限制数量不可大于商品库存",
"limit": "兑换限制",
"limitPlaceholder": "请输入兑换限制",
"limitTips": "[兑换限制]格式错误",
"limitTipsTwo": "兑换限制数量不可小于等于0",
"limitTipsThree": "兑换限制数量不能大于兑换库存",
"limitUnit": "件/人",
"integralUnit": "积分",
"prickUnit": "元",
"pointPlaceholder": "请输入积分积分",
"pointTips": "[积分]格式错误",
"pointTipsTwo": "积分不可小于等于0",
"money": "兑换价",
"newPrice": "价格",
"moneyTips": "[价格]格式错误",
"moneyTipsTwo": "价格不可小于0",
"limitRules": "每人每单可兑换件数",
"couponSelect": "选择优惠券",
"couponSelectPlaceholder": "请选择优惠券",
"couponName": "优惠券名称",
"couponType": "类型",
"couponPrice": "面值",
"sumCount": "总库存",
"threshold": "使用门槛",
"validType": "有效期",
"receiveTypeTime": "领取有效期",
"balance": "兑换余额",
"balancePlaceholder": "请输入兑换余额",
"balanceTips": "[兑换余额]格式错误",
"balanceTipsTwo": "兑换余额不可小于等于0",
"balanceType": "余额类型",
"goodsSkuTitle": "选择商品规格",
"goodsSkuPlaceholder": "请选择商品规格",
"goodsStock": "库存",
"enabled": "设为参与",
"noEnabled": "设为不参与",
"noEnabledTip": "请至少设置一个规格参与",
"goodsDetail": "商品详情"
}

View File

@ -0,0 +1,33 @@
{
"addGoods": "添加商品",
"goods": "商品",
"goodsName": "商品名称",
"goodsNamePlaceholder": "请输入商品名称",
"status": "状态",
"statusPlaceholder": "请选择状态",
"goodsType": "商品类型",
"exchangePrice": "兑换价格",
"pointUnit": "积分",
"priceUnit": "元",
"redeemedAndSurplus": "已兑/剩余",
"deleteTips": "是否确认删除当前商品",
"createTime": "创建时间",
"spreadGoods": "推广",
"copyGoods": "复制",
"spreadLink": "推广链接",
"copy": "复制",
"downloadQrcode": "下载二维码",
"goodsSpreadTitle": "积分商品推广",
"up": "上架",
"upTips": "是否确认上架当前商品",
"down": "下架",
"downTips": "是否确认下架当前商品",
"startDate": "开始时间",
"endDate": "结束时间",
"batchUpTips":"是否确认批量上架选中的商品?",
"batchDownTips":"是否确认批量下架选中的商品?",
"batchDeleteTips":"是否确认批量删除选中的商品?",
"batchUp":"批量上架",
"batchDown":"批量下架",
"batchDelete":"批量删除"
}

View File

@ -0,0 +1,53 @@
{
"orderNo": "订单编号",
"orderNoPlaceholder": "请输入订单编号",
"orderStatus": "订单状态",
"orderStatusPlaceholder": "请选择订单状态",
"orderFrom": "订单类型",
"orderFromPlaceholder": "请选择订单类型",
"payTime": "支付时间",
"orderGoods": "商品",
"goodsPriceNumber": "单价(元)/数量",
"orderMoney": "实付金额(元)",
"startDate": "开始时间",
"endDate": "结束时间",
"piece": "件",
"activeRefund": "主动退款",
"notes": "备注",
"offlinePayment": "线下支付",
"orderClose": "关闭订单",
"editPrice": "修改价格",
"editAddress": "修改地址",
"sendOutGoods": "发货",
"confirmTakeDelivery": "确认收货",
"all": "全部",
"toBeShipped": "待发货",
"shipped": "已发货",
"receivedGoods": "已收货",
"completed": "已完成",
"closed": "已关闭",
"refunding": "退款中",
"notesDetail": "备注信息",
"delivery": "订单发货",
"company": "物流公司",
"companyPlaceholder": "请选择物流公司",
"expressNumber": "物流单号",
"expressNumberPlaceholder": "请输入物流单号",
"orderGoodsIdsPlaceholder": "请选择订单项",
"virtualDelivery": "虚拟发货",
"goodsName": "商品名称",
"num": "商品数量",
"orderCloseTips": "关闭订单后该订单将无法支付,是否确认关闭?",
"orderFinishTips": "是否确认用户已经收货?",
"orderGoodsPlaceholder": "请选择要发货的商品",
"deliveryStatusName": "发货状态",
"fromType": "订单来源",
"payType": "支付类型",
"orderInfo": "订单信息",
"refundStatusName": "退款状态",
"outTradeNo": "交易流水号",
"exportOrderType": "导出订单类型",
"shopOrder": "订单数据表",
"shopOrderGoods": "订单商品表",
"point": "积分"
}

View File

@ -0,0 +1,13 @@
{
"rankName": "榜单标题",
"rankImages": "榜单图片",
"rankRemark": "榜单说明",
"save": "保存",
"imagePlaceholder": "请上传榜单图片",
"rankNamePlaceholder": "请输入榜单名称",
"rankRemarkPlaceholder": "请输入榜单说明",
"useDefaultRankRemark": "使用默认说明",
"noColor": "常规颜色",
"selectedColor": "选中文字颜色",
"selectedBgColor": "选中背景颜色"
}

View File

@ -0,0 +1,33 @@
{
"addRanking": "添加榜单",
"updateRanking": "修改榜单",
"rankName": "榜单名称",
"rankBanner": "榜单banner",
"imagePlaceholder": "请上传图片",
"rankNamePlaceholder": "请输入榜单名称",
"rankType": "排行周期",
"rankTypePlaceholder": "请选择榜单周期",
"batchDeletion": "批量删除",
"goodsSource": "商品来源",
"goodsSourcePlaceholder": "请选择商品来源",
"addCategory": "添加分类",
"categoryTips": "商品分类不能为空",
"brand": "商品品牌",
"brandPlaceholder": "请选择商品品牌",
"brandTips": "商品品牌不能为空",
"addBrand": "添加品牌",
"label": "商品标签",
"labelTips": "商品标签不能为空",
"addLabel": "添加商品标签",
"sort": "排序",
"sortPlaceholder": "请输入排序号",
"ruleType": "排序规则",
"ruleTypePlaceholder": "请选择排序规则",
"selectGoods": "选择商品",
"sortTips": "排序号格式输入错误",
"goodsJsonEmpty": "商品数据不能为空",
"limitTips": "输入格式不正确",
"rankTypeTips": "天1天7天30天季度90天",
"sortRules": "从大到小排列,支持手动修改排序号调整排名,以满足特定需求展示效果",
"isShow": "状态"
}

View File

@ -0,0 +1,17 @@
{
"addRanking": "添加榜单",
"rankName": "榜单名称",
"rankType": "排行周期",
"rankNamePlaceholder": "请输入榜单名称",
"batchDeletion": "批量删除",
"showGoodsNum": "榜单商品数量",
"goodsSource": "商品来源",
"ruleType": "排序规则",
"createTime": "创建时间",
"deleteTips": "确定删除该榜单吗?",
"batchEmptySelectedGoodsTips": "请先选择要删除的榜单",
"batchGoodsDeleteTips": "确定删除选中的榜单吗?",
"sortTips": "排序号格式输入错误",
"sortRules": "按照排序号从大到小排序",
"isShow": "状态"
}

View File

@ -0,0 +1,37 @@
{
"baseInfo": "基础信息",
"name": "活动名称",
"title": "标题",
"status": "状态",
"paymentAmount": "支付金额",
"memberCount": "参与会员数",
"orderCount": "支付订单数",
"createTime": "创建时间",
"startTime": "活动开始时间",
"endTime": "活动结束时间",
"keywordPlaceholder": "请输入商品名称",
"keyword": "商品名称",
"goodsInfo": "商品信息",
"price": "商品价格",
"activeOrderMoney": "累计订单金额",
"activeOrderNum": "累计订单数",
"activeMemberNum": "参与会员数",
"activeSuccessNum": "支付销量",
"orderInfo": "订单编号",
"payTime": "支付时间",
"orderNo": "订单编号",
"orderMoney": "订单金额",
"buyInfo": "买家/收货人",
"payType": "支付类型",
"orderStatus": "订单状态",
"toBePaid": "待支付",
"toBeShipped": "待发货",
"shipped": "已发货",
"receivedGoods": "已收货",
"completed": "已完成",
"closed": "已关闭",
"consumptionMoney": "消费总额",
"participationNum": "参与次数",
"orderTime": "最后下单时间",
"memberInfo": "会员信息"
}

View File

@ -0,0 +1,88 @@
{
"addFullDiscountBonus": "添加满减送",
"editFullDiscountBonus": "修改满减送",
"activeName": "活动名称",
"activityTime": "活动时间",
"namePlaceholder": "请输入活动名称",
"conditionType": "条件类型",
"overNyuan": "满N元",
"overNgoods": "满N件",
"ruleType": "优惠规则",
"ladder": "阶梯优惠",
"cycle": "循环优惠",
"ruleJson": "优惠设置",
"activityLevel": "活动层级",
"addActivityLevel": "添加活动层级",
"discountThreshold": "优惠门槛",
"placeholder": "请输入",
"delete": "删除",
"discountContent": "优惠内容",
"discountMoney": "订单金额优惠",
"reduce": "减价",
"discountRate": "打折",
"freeShipping": "包邮",
"freeTips": "仅参与该活动的商品包邮,非整单包邮",
"givePoint": "送积分",
"giveGrowth": "送成长值",
"giveBalance": "送余额",
"giveCoupon": "送优惠券",
"giveCouponTips": "如果优惠券数量不足、限领数量已达上限或优惠券状态不是“进行中”,都将导致赠送失败。",
"name": "优惠券名称",
"type": "优惠券类型",
"couponPrice": "优惠券面值",
"couponGiveNum": "赠券数",
"giveGoods": "送赠品",
"giveGoodsTips": "当赠品库存不足或下架时则不赠送",
"stock": "库存",
"giveNum": "赠送数量",
"joinMemberType": "活动对象",
"allMember": "所有会员参与",
"selectedMemberLevel": "指定会员等级",
"selectedMemberLabel": "指定会员标签",
"memberLevel": "会员等级",
"memberLevelPlaceholder": "请选择会员等级",
"memberLabel": "会员标签",
"memberLabelPlaceholder": "请选择会员标签",
"goodsType": "参与商品",
"allGoods": "全部商品参与",
"selectedGoods": "指定商品参与",
"selectedGoodsNot": "指定商品不参与",
"selectGoods": "选择商品",
"remark": "备注",
"rankRemarkPlaceholder": "请输入备注",
"selectActivityTime": "请选择活动时间",
"selectActivityStartTime": "请选择活动开始时间",
"selectActivityEndTime": "请选择活动结束时间",
"selectActivityTimeTips": "活动结束时间不能小于等于活动开始时间",
"addLevelLimit": "层级限制最多添加五个",
"labelTips": "请选择会员标签",
"levelTips": "请选择会员等级",
"goodsJsonEmpty": "请选择商品",
"couponPlaceholder": "请选择优惠券",
"limitPlaceholder": "请输入优惠门槛",
"discountsPlaceholder": "请输入折扣或金额",
"growthPlaceholder": "请输入赠送成长值",
"pointPlaceholder": "请输入赠送积分",
"balancePlaceholder": "请输入赠送余额",
"valueMustBeGreaterThanZero": "优惠门槛必须大于0",
"pointMustBeGreaterThanZero": "赠送积分必须大于0",
"balanceMustBeGreaterThanZero": "赠送余额必须大于0",
"growthMustBeGreaterThanZero": "赠送成长值必须大于0",
"discountMustBeGreaterThanZero": "优惠内容必须大于0",
"discountMustBeGreaterThanNine": "范围大于等于0.1且小于等于9.9",
"limitTips": "输入格式不正确",
"limitTipsTwo": "每个层级的门槛金额不能相同",
"addSuccess": "添加成功",
"updateSuccess": "编辑成功",
"addFail": "添加失败",
"updateFail": "编辑失败",
"goodOnlyOne": "同一商品在一个时间段内只能参加一个满减送活动",
"discountLimit": "优惠金额不能大于优惠门槛",
"giveNumPlaceholder": "请输入赠送数量",
"giveNumMustBeGreaterThanZero": "赠送数量必须大于0",
"giveNumMustBeGreaterThanStock": "赠送数量不能大于库存",
"limitTipsThree": "必须大于上一层级的优惠门槛",
"ruleTypeTips": "阶梯优惠根据设定的门槛逐级增加优惠达到特定层级门槛即可享受对应优惠最高可设5个层级。",
"ruleTypeTipsTwo": "循环优惠指每次达到门槛即获赠一次优惠,无次数上限,需合理设置门槛。",
"goodsOffTips": "该商品已下架"
}

View File

@ -0,0 +1,58 @@
{
"addFullDiscountBonus": "添加满减送",
"name": "活动名称",
"detailTitle": "满减送活动详情",
"activeDetail": "活动详情",
"namePlaceholder": "请输入活动名称",
"title": "标题",
"status": "状态",
"statusPlaceholder": "请选择状态",
"activityTime": "活动时间",
"detail": "详情",
"close": "关闭",
"closeTips": "活动正在进行中,是否确认关闭当前活动?",
"deleteTips": "是否确认删除当前活动",
"createTime": "创建时间",
"startTime": "活动开始时间",
"endTime": "活动结束时间",
"keywordPlaceholder": "请输入商品名称",
"keyword": "商品名称",
"price": "商品价格",
"activeOrderMoney": "累计订单金额",
"activeOrderNum": "支付订单数",
"activeMemberNum": "参与会员数",
"paymentAmount": "支付金额",
"memberCount": "会员数",
"orderCount": "订单数",
"activeSuccessNum": "支付销量",
"activeName": "活动名称",
"participationMemberCount": "参与会员数",
"startDate": "开始时间",
"endDate": "结束时间",
"all": "全部",
"orderTime": "最后下单时间",
"consumptionMoney": "消费总额",
"memberInfo": "会员信息",
"participationNum": "参与次数",
"goodsInfo": "商品信息",
"discountThreshold": "优惠门槛",
"giveGoods": "送赠品",
"givePoint": "送积分",
"giveBalance": "送余额",
"giveGrowth": "送成长值",
"giveCoupon": "送优惠券",
"discountMoney": "订单优惠",
"freeShipping": "包邮",
"basicInfo": "基本信息",
"totalPoint": "累计赠送积分",
"totalBalance": "累计赠送余额",
"totalCouponNum": "累计赠送优惠券",
"totalGoodsNum": "累计赠送商品数",
"activeMember": "活动会员",
"batchDelete": "批量删除",
"batchClose": "批量关闭",
"batchEmptySelectedActiveDeleteTips": "请先选择要删除的活动",
"batchEmptySelectedActiveCloseTips": "请先选择要关闭的活动",
"batchGoodsDeleteTips": "确定删除选中的活动吗?",
"batchGoodsCloseTips": "确定关闭选中的活动吗?"
}

View File

@ -0,0 +1,45 @@
{
"basicInfoTab": "基础设置",
"bannerList": "顶部广告图",
"activeStatus": "是否启用",
"validityType": "有效期",
"validityDay": "天数",
"validityDayPlaceholder": "请输入有效天数",
"validityDayTips": "有效天数不可小于等于0",
"validityDayTipsLeft": "达成门槛立即生效,",
"validityDayTipsRight": "天有效期",
"validityTime": "固定时间",
"validityTimePlaceholder": "请选择有效期截止时间",
"validityTimePlaceholderTwo": "请先选择参与门槛时间",
"validityTimePlaceholderThree": "有效期截止时间不可小于参与门槛时间",
"validityTimeTips": "达成门槛立即生效,有效期截止为",
"validityTimeTipsTwo": "修改有效期将同步更新所有未参与活动用户的结束时间",
"participationWay": "参与门槛",
"neverOrder": "从未下过单的会员",
"assignTimeOrder": "指定时间内未下过单的会员",
"assignTimeRegister": "指定时间内注册的会员",
"appointTimePlaceholder": "请选择指定时间",
"activityGoods": "活动商品",
"selectGoods": "选择商品",
"goodsSkuIdsPlaceholder": "请选择商品",
"limitNum": "限购数量",
"limitNumPlaceholder": "请输入限购数量",
"limitNumTips": "限购数量不可小于等于0",
"limitNumTipsThree": "限购数量不可超过已选商品数量",
"oldPrice": "原价",
"newcomerPrice": "新人价",
"newcomerPricePlaceholder": "请输入新人价",
"newcomerPriceTips": "[新人价]格式错误",
"newcomerPriceTipsOne": "新人价不可小于0",
"newcomerPriceTipsTwo": "新人价不可大于原价",
"batchOperation": "批量操作",
"batchEmptySelectedGoodsTips": "请选择要操作的商品",
"activeDesc": "规则说明",
"activeDescPlaceholder": "请输入规则说明",
"useDefaultActiveDesc": "使用默认说明",
"image": "图片上传",
"imagePlaceholder": "请上传图片",
"toLink": "跳转链接",
"toLinkPlaceholder": "请输入跳转链接",
"addConfigList": "添加广告图"
}

View File

@ -0,0 +1,55 @@
{
"orderNo": "订单编号",
"orderNoPlaceholder": "请输入订单编号",
"orderStatus": "订单状态",
"orderStatusPlaceholder": "请选择订单状态",
"orderFrom": "订单类型",
"orderFromPlaceholder": "请选择订单类型",
"payTime": "支付时间",
"orderGoods": "商品",
"goodsPriceNumber": "单价(元)/数量",
"goodsPriceNumberTips": "新人价商品购买数量为1时单价显示新人价购买数量大于1时单价显示原价或折扣价或会员价",
"orderMoney": "实付金额(元)",
"startDate": "开始时间",
"endDate": "结束时间",
"piece": "件",
"createTime": "创建时间",
"activeRefund": "主动退款",
"notes": "备注",
"offlinePayment": "线下支付",
"orderClose": "关闭订单",
"editPrice": "修改价格",
"editAddress": "修改地址",
"sendOutGoods": "发货",
"confirmTakeDelivery": "确认收货",
"all": "全部",
"toBeShipped": "待发货",
"shipped": "已发货",
"receivedGoods": "已收货",
"completed": "已完成",
"closed": "已关闭",
"refunding": "退款中",
"notesDetail": "备注信息",
"delivery": "订单发货",
"company": "物流公司",
"companyPlaceholder": "请选择物流公司",
"expressNumber": "物流单号",
"expressNumberPlaceholder": "请输入物流单号",
"orderGoodsIdsPlaceholder": "请选择订单项",
"virtualDelivery": "虚拟发货",
"goodsName": "商品名称",
"num": "商品数量",
"orderCloseTips": "关闭订单后该订单将无法支付,是否确认关闭?",
"orderFinishTips": "是否确认用户已经收货?",
"orderGoodsPlaceholder": "请选择要发货的商品",
"deliveryStatusName": "发货状态",
"fromType": "订单来源",
"payType": "支付类型",
"orderInfo": "订单信息",
"refundStatusName": "退款状态",
"outTradeNo": "交易流水号",
"exportOrderType": "导出订单类型",
"shopOrder": "订单数据表",
"shopOrderGoods": "订单商品表",
"point": "积分"
}

View File

@ -0,0 +1,25 @@
{
"failuresNum": "发货失败单数",
"succeedNum": "发货成功单数",
"totalNum": "总发货单数",
"operationTime": "操作时间",
"operationType": "操作类型",
"state": "状态",
"operator": "操作人",
"importData": "批量发货",
"templateType": "模板类型",
"fullOrderDelivery": "整单发货模板",
"openOrderDelivery": "拆单发货模板",
"uploadFile": "上传文件",
"createTime": "时间",
"uploadFilePlaceholder": "请上传文件",
"operatorPlaceholder": "请选择操作人",
"causeFailure": "下载失败记录",
"downloadRecord": "下载记录",
"orderTemplate": "下载整单发货模板.xls",
"orderGoodsTemplate": "下载拆单发货模板.xls",
"checkCause": "查看失败原因",
"startTime": "开始时间",
"endTime": "结束时间",
"checkCause1": "失败原因"
}

View File

@ -0,0 +1,40 @@
{
"closeOrderInfo": "自动取消订单",
"closeOrderInfoLeft": "提交订单",
"closeOrderInfoRight": "分钟未付款,自动取消订单",
"closeOrderInfoBottom": "订单取消时间必须在10-1440分钟之间",
"CloseLengthPlaceholder": "请输入订单取消时间",
"isClose": "自动取消订单",
"confirm": "自动确认收货",
"confirmLeft": "发货后",
"confirmRight": "天,自动确认收货",
"confirmBottom": "自动确认收货时间必须在1-30天之间",
"finishLengthPlaceholder": "请输入自动确认收货时间",
"isFinish": "自动确认收货",
"refund": "确认收货后售后",
"refundLeft": "确认收货",
"refundRight": "天内,可申请售后",
"refundBottom": "确认收货后售后时间必须在1-30天之间",
"noAllowRefund": "确认收货后不支持售后",
"validRefundLengthPlaceholder": "请输入确认收货后售后时间",
"invoice": "发票设置",
"isInvoice": "发票开关",
"isInvoiceClose": "关闭",
"isInvoiceOpen": "开启",
"invoiceContent": "发票内容",
"insert": "添加",
"paperInvoice": "纸质发票",
"electronicInvoice": "电子发票",
"invoicePlaceholder": "所有发票内容不能为空",
"invoiceType": "发票类型",
"invoiceTypePlaceholder": "请至少选择一个发票类型",
"evaluate": "评价设置",
"isEvaluate": "评价开关",
"evaluateIsToExamine": "评价审核",
"evaluateIsShow": "评价显示",
"isEvaluateOpen": "开启",
"isEvaluateClose": "关闭",
"diyForm": "万能表单",
"diyFormPlaceholder": "请选择万能表单",
"addDiyForm": "添加表单"
}

View File

@ -0,0 +1,145 @@
{
"orderNo": "订单编号",
"orderInfo": "订单信息",
"outTradeNo": "交易流水号",
"orderType": "订单类型",
"orderForm": "订单来源",
"takerName": "收货人",
"takerMobile": "收货人手机号",
"takerFullAddress": "收货地址",
"goodsDetail": "商品信息",
"goodsName": "商品名称",
"price": "价格",
"num": "数量",
"goodsMoney": "商品总额",
"preferentialMoney": "优惠金额",
"deliveryMoney": "配送金额",
"operateLog": "订单日志",
"orderNoPlaceholder": "请输入订单编号",
"orderStatus": "订单状态",
"orderStatusPlaceholder": "请选择订单状态",
"orderFrom": "订单类型",
"orderFromPlaceholder": "请选择订单类型",
"payTime": "支付时间",
"orderGoods": "商品",
"goodsPriceNumber": "单价(元)/数量",
"orderMoney": "订单金额",
"buyInfo": "买家/收货人",
"deliveryType": "配送方式",
"startDate": "开始时间",
"endDate": "结束时间",
"piece": "件",
"payType": "支付方式",
"notes": "备注",
"editAddress": "修改地址",
"remind": "提醒",
"remindTips1": "买家付款成功后,货款将直接进入您的商户号(微信、支付宝)",
"remindTips2": "请及时关注你发出的包裹状态,确保可以配送至买家手中",
"remindTips3": "如果买家表示没收到货或货物有问题,请及时联系买家处理,友好协商",
"close": "关闭订单",
"finish": "确认收货",
"delivery": "订单发货",
"deliveryTypePlaceholder": "请选择配送方式",
"company": "物流公司",
"companyPlaceholder": "请选择物流公司",
"expressNumber": "物流单号",
"expressNumberPlaceholder": "请输入物流单号",
"orderGoodsIdsPlaceholder": "请选择订单项",
"virtualDelivery": "虚拟发货",
"orderCloseTips": "关闭订单后该订单将无法支付,是否确认关闭?",
"orderFinishTips": "是否确认用户已经收货?",
"orderGoodsPlaceholder": "请选择要发货的商品",
"memberRemark": "买家留言",
"discountMoney": "总优惠金额",
"couponMoney": "优惠券优惠金额",
"manjianDiscountMoney": "满减优惠金额",
"orderDelivery": "物流信息",
"devliveryTime": "发货时间",
"companyName": "物流公司",
"logisticNo": "物流单号",
"packageInfo": "物流包裹信息",
"deliveryInfo": "发货信息",
"goodsInfo": "商品信息",
"logisticInfo": "物流信息",
"storeName": "自提点名称",
"storeAddress": "自提点地址",
"storeMobile": "自提点电话",
"tradeTime": "营业时间",
"deliveryStatusName": "发货状态",
"package": "包裹",
"noLogisticsRequired": "无需物流",
"notLogistics": "暂无物流信息",
"refundStatusName": "退款状态",
"orderInfoEmpty": "暂无数据",
"editPrice": "修改价格",
"adjustMoneyDialogTitle": "调整价格",
"adjustMoneyUnit": "元",
"adjustMoneyTips": "注意 : 只有订单未付款时才支持改价,改价后请联系买家刷新订单核实订单金额后再支付。当订单总额为0元时订单将自动支付",
"adjustMoneyDeliveryMoney": "运费",
"adjustMoneyGoodsInfo": "商品信息",
"adjustMoneyPrice": "单价",
"adjustMoneyNum": "数量",
"adjustMoneySubTotal": "小计",
"adjustMoneyDiscountMoney": "优惠金额",
"adjustMoneyLabel": "调整金额",
"adjustMoneyTotal": "总计",
"point": "积分",
"electronicSheetPrintTitle": "打印电子面单",
"electronicSheetTemplate": "面单模板",
"electronicSheetTemplatePlaceholder": "请选择面单模板",
"electronicSheetPrintResult": "打印结果",
"deliveryPackageNo": "包裹编号",
"printStatus": "状态",
"printResultCode": "状态码",
"printRemark": "备注",
"electronicSheetPrintOperation": "打印面单",
"printTicket": "打印小票",
"deliveryWay": "发货方式",
"manualWriteWay": "手动填写",
"electronicSheetWay": "电子面单",
"contacts": "联系人",
"contactsPlaceholder": "请输入联系人",
"ContactInformation": "联系方式",
"ContactInformationPlaceholder": "请输入联系方式",
"selfPickupStores": "自提门店",
"address": "地址",
"province": "请选择省",
"city": "请选择市",
"area": "请选择区/县",
"detailedAddress": "请输入详细地址",
"refundTitle": "商家主动退款",
"refundMoney": "退款金额",
"voluntaryRefund": "主动退款",
"refundState": "完成状态",
"refundType": "退款方式",
"refundInstructions": "退款说明",
"refundInstructionsOne": "1、如果是退部分金额退款后可以是部分退款状态或退款完成状态",
"refundInstructionsTwo": "2、如果是退全部金额则退款后一定是退款完成状态",
"refundInstructionsThree": "3、退款完成才会执行相关业务如核销码失效卡包失效等操作",
"backRefund": "原路退款",
"offlineRefund": "线下退款",
"refundToBalance": "退款到余额",
"partialRefund": "部分退款状态",
"refundFinish": "退款完成状态",
"shopActiveRefundMoneyPlaceholder": "主动退款金额不能为空或为零",
"shopActiveRefundMoneyTwoPlaceholder": "主动退款金额不能大于可退款总额",
"refundGoodsItem": "商品项",
"refundGoodsInfo": "商品信息",
"refundGoodsSku": "规格",
"refundGoodsPrice": "售价",
"refundPayPrice": "实付金额",
"refundGoodsNum": "数量",
"refundGoodsPlaceholder": "请选择退款的商品",
"formDetail": "表单详情",
"buyerAskDeliveryTime": "要求自提时间",
"verifyCode": "核销码",
"buyers": "买家",
"buyersReserveMobile": "预留手机",
"storeTakerName": "提货人",
"verifierMember": "核销人",
"buyerAskDeliveryTime2":"预约配送时间",
"deliveryTransporterMobile": "配送员手机号",
"deliveryTransporterName": "配送员",
"deliveryTransporterStatus":"配送状态",
"deliveryTransporterInfo":"配送信息"
}

View File

@ -0,0 +1,40 @@
{
"isInvoice": "是否开票",
"isInvoicePlaceholder": "请输入开票类型",
"startDate": "开始时间",
"endDate": "结束时间",
"headType": "抬头类型",
"headerTypeName": "抬头类型",
"person": "个人",
"firm": "企业",
"headTypePlaceholder": "请输入抬头类型",
"headerName": "发票抬头",
"headerNamePlaceholder": "请输入发票抬头",
"name": "发票内容",
"tradeType": "发票类型",
"typeName": "发票名称",
"headTypeName": "抬头类型",
"taxNumber": "纳税人识别号",
"mobile": "手机号",
"email": "邮件",
"telephone": "电话号",
"address": "地址",
"bankTame": "开户银行",
"bankCardNumber": "开户行账号",
"money": "开票金额",
"invoiceNumber": "发票号码",
"invoiceVoucher": "发票凭证",
"remark": "备注",
"createTime": "创建时间",
"invoiceTime": "开票时间",
"status": "状态",
"operation": "操作",
"detail": "详情",
"hasInvoice": "已开票",
"noInvoice": "未开票",
"all": "全部",
"invoice": "开票",
"viewOrder": "查看订单",
"invoiceNumberPlaceholder": "请输入发票号码",
"invoiceVoucherPlaceholder": "请输入发票凭证"
}

View File

@ -0,0 +1,164 @@
{
"orderNo": "订单编号",
"createTime": "下单时间",
"orderNoPlaceholder": "请输入订单编号",
"memberInfo": "会员信息",
"memberInfoPlaceholder": "请输入会员编号/昵称/账号/手机号搜索",
"orderStatus": "订单状态",
"orderStatusPlaceholder": "请选择订单状态",
"orderFrom": "订单类型",
"orderFromPlaceholder": "请选择订单类型",
"payTime": "支付时间",
"orderGoods": "商品",
"goodsPriceNumber": "单价(元)/数量",
"orderMoney": "实付金额(元)",
"point": "积分",
"buyInfo": "买家/收货人",
"deliveryType": "配送方式",
"startDate": "开始时间",
"endDate": "结束时间",
"piece": "件",
"rightsProtection": "售后",
"activeRefund": "主动退款",
"notes": "备注",
"offlinePayment": "线下支付",
"orderClose": "关闭订单",
"editPrice": "修改价格",
"editAddress": "修改地址",
"sendOutGoods": "发货",
"confirmTakeDelivery": "确认收货",
"all": "全部",
"toBePaid": "待支付",
"toBeShipped": "待发货",
"shipped": "已发货",
"receivedGoods": "已收货",
"completed": "已完成",
"closed": "已关闭",
"refunding": "退款中",
"notesDetail": "备注信息",
"deliveryTypePlaceholder": "请选择配送方式",
"company": "物流公司",
"companyPlaceholder": "请选择物流公司",
"expressNumber": "物流单号",
"expressNumberPlaceholder": "请输入物流单号",
"orderGoodsIdsPlaceholder": "请选择订单项",
"virtualDelivery": "虚拟发货",
"goodsName": "商品名称",
"num": "商品数量",
"orderCloseTips": "关闭订单后该订单将无法支付,是否确认关闭?",
"orderFinishTips": "是否确认用户已经收货?",
"orderGoodsPlaceholder": "请选择要发货的商品",
"deliveryStatusName": "发货状态",
"fromType": "订单来源",
"payType": "支付类型",
"orderInfo": "订单信息",
"refundStatusName": "退款状态",
"outTradeNo": "交易流水号",
"exportOrderType": "导出订单类型",
"shopOrder": "订单数据表",
"shopOrderGoods": "订单商品表",
"adjustMoneyDialogTitle": "调整价格",
"adjustMoneyUnit": "元",
"adjustMoneyTips": "注意 : 只有订单未付款时才支持改价,改价后请联系买家刷新订单核实订单金额后再支付。当订单总额为0元时订单将自动支付",
"adjustMoneyDeliveryMoney": "运费",
"adjustMoneyGoodsInfo": "商品信息",
"adjustMoneyPrice": "单价",
"adjustMoneyNum": "数量",
"adjustMoneySubTotal": "小计",
"adjustMoneyDiscountMoney": "优惠金额",
"adjustMoneyLabel": "调整金额",
"adjustMoneyTotal": "总计",
"contacts": "联系人",
"contactsPlaceholder": "请输入联系人",
"ContactInformation": "联系方式",
"ContactInformationPlaceholder": "请输入联系方式",
"address": "地址",
"province": "请选择省",
"city": "请选择市",
"area": "请选择区/县",
"detailedAddress": "请输入详细地址",
"selfPickupStores": "自提门店",
"electronicSheetPrintTitle": "打印电子面单",
"electronicSheetTemplate": "面单模板",
"electronicSheetTemplatePlaceholder": "请选择面单模板",
"electronicSheetPrintResult": "打印结果",
"deliveryPackageNo": "包裹编号",
"batchPrintElectronicSheet": "批量打印电子面单",
"batchEmptySelectedOrderTips": "请选择要操作的订单",
"notSupportPrintElectronicSheetTips": "当前选择的订单不支持打印电子面单",
"printStatus": "状态",
"printResultCode": "状态码",
"printRemark": "备注",
"electronicSheetPrintOperation": "打印面单",
"printTicket": "打印小票",
"deliveryWay": "发货方式",
"manualWriteWay": "手动填写",
"electronicSheetWay": "电子面单",
"refundTitle": "商家主动退款",
"refundMoney": "退款金额",
"voluntaryRefund": "主动退款",
"refundState": "完成状态",
"refundType": "退款方式",
"refundInstructions": "退款说明",
"refundInstructionsOne": "1、如果是退部分金额退款后可以是部分退款状态或退款完成状态",
"refundInstructionsTwo": "2、如果是退全部金额则退款后一定是退款完成状态",
"refundInstructionsThree": "3、退款完成才会执行相关业务如核销码失效卡包失效等操作",
"backRefund": "原路退款",
"offlineRefund": "线下退款",
"refundToBalance": "退款到余额",
"partialRefund": "部分退款状态",
"refundFinish": "退款完成状态",
"shopActiveRefundMoneyPlaceholder": "主动退款金额不能为空或为零",
"shopActiveRefundMoneyTwoPlaceholder": "主动退款金额不能大于可退款总额",
"refundGoodsItem": "商品项",
"refundGoodsInfo": "商品信息",
"refundGoodsSku": "规格",
"refundGoodsPrice": "售价",
"refundPayPrice": "实付金额",
"refundGoodsNum": "数量",
"refundGoodsPlaceholder": "请选择退款的商品",
"orderType": "订单类型",
"orderForm": "订单来源",
"takerName": "收货人",
"takerMobile": "收货人手机号",
"takerFullAddress": "收货地址",
"goodsDetail": "商品信息",
"price": "价格",
"detailNum": "数量",
"goodsMoney": "商品总额",
"preferentialMoney": "优惠金额",
"deliveryMoney": "配送金额",
"operateLog": "订单日志",
"detailOrderMoney": "订单金额",
"payWay": "支付方式",
"remind": "提醒",
"remindTips1": "买家付款成功后,货款将直接进入您的商户号(微信、支付宝)",
"remindTips2": "请及时关注你发出的包裹状态,确保可以配送至买家手中",
"remindTips3": "如果买家表示没收到货或货物有问题,请及时联系买家处理,友好协商",
"close": "关闭订单",
"finish": "确认收货",
"delivery": "订单发货",
"memberRemark": "买家留言",
"discountMoney": "总优惠金额",
"couponMoney": "优惠券优惠金额",
"manjianDiscountMoney": "满减优惠金额",
"orderDelivery": "物流信息",
"devliveryTime": "发货时间",
"companyName": "物流公司",
"logisticNo": "物流单号",
"packageInfo": "物流包裹信息",
"deliveryInfo": "发货信息",
"goodsInfo": "商品信息",
"logisticInfo": "物流信息",
"storeName": "自提点名称",
"storeAddress": "自提点地址",
"storeMobile": "自提点电话",
"tradeTime": "营业时间",
"package": "包裹",
"noLogisticsRequired": "无需物流",
"notLogistics": "暂无物流信息",
"orderInfoEmpty": "暂无数据",
"buyerAskDeliveryTime": "要求自提时间",
"deleteTips": "是否确认删除该订单?",
"batchDeleteTips": "是否确认批量删除选中的订单?"
}

View File

@ -0,0 +1,108 @@
{
"goodsName": "商品名称",
"orderNo": "订单编号",
"orderRefundNo": "退款编号",
"refundTime": "退款时间",
"startDate": "开始时间",
"endDate": "结束时间",
"goodsNamePlaceholder": "请输入商品名称",
"orderNoPlaceholder": "请输入订单编号",
"orderRefundNoPlaceholder": "请输入退款编号",
"goodsInfo": "商品信息",
"orderMoney": "订单金额",
"realityMoney": "实付金额",
"goodsMoney": "商品金额",
"buyMember": "买家",
"refundMoney": "退款金额",
"createTime": "申请时间",
"refundStatus": "退款状态",
"all": "全部",
"refundType": "退款方式",
"applyForRefund": "申请退款",
"refundEnd": "售后结束",
"toBeReturned": "买家待退货",
"receivedGoods": "卖家待收货",
"refundRefuse": "卖家拒绝",
"orderInfo": "订单信息",
"outTradeNo": "交易流水号",
"orderType": "订单类型",
"orderForm": "订单来源",
"takerName": "收货人",
"takerMobile": "收货人手机号",
"takerFullAddress": "收货地址",
"goodsDetail": "商品信息",
"price": "价格",
"num": "数量",
"preferentialMoney": "优惠金额",
"deliveryMoney": "配送金额",
"operateLog": "订单日志",
"orderStatus": "订单状态",
"orderStatusPlaceholder": "请选择订单状态",
"orderFrom": "订单类型",
"orderFromPlaceholder": "请选择订单类型",
"payTime": "支付时间",
"orderGoods": "商品",
"goodsPriceNumber": "单价(元)/数量",
"detailOrderMoney": "实付金额(元)",
"buyInfo": "买家/收货人",
"deliveryType": "配送方式",
"piece": "件",
"payType": "支付方式",
"notes": "备注",
"editAddress": "修改地址",
"remind": "提醒",
"remindTips1": "如果未发货,请点击同意退款给买家。",
"remindTips2": "如果实际已发货,请主动与买家联系。",
"remindTips3": "如果订单整体退款后,优惠券和余额会退还给买家。",
"close": "关闭订单",
"finish": "确认收货",
"delivery": "订单发货",
"deliveryTypePlaceholder": "请选择配送方式",
"company": "物流公司",
"companyPlaceholder": "请选择物流公司",
"expressNumber": "物流单号",
"expressNumberPlaceholder": "请输入物流单号",
"orderGoodsIdsPlaceholder": "请选择订单项",
"virtualDelivery": "虚拟发货",
"orderCloseTips": "关闭订单后该订单将无法支付,是否确认关闭?",
"orderFinishTips": "是否确认用户已经收货?",
"orderGoodsPlaceholder": "请选择要发货的商品",
"memberRemark": "买家留言",
"discountMoney": "订单详情",
"orderDelivery": "物流信息",
"devliveryTime": "发货时间",
"companyName": "物流公司",
"logisticNo": "物流单号",
"packageInfo": "物流包裹信息",
"deliveryInfo": "发货信息",
"logisticInfo": "物流信息",
"storeName": "自提点名称",
"storeAddress": "自提点地址",
"storeMobile": "自提点电话",
"tradeTime": "营业时间",
"deliveryStatusName": "发货状态",
"refundReason": "退款原因",
"afterSales": "售后信息",
"orderRefundRefuse": "退款拒绝",
"orderRefundAgree": "同意退款",
"agree": "同意",
"applyMoney": "申请金额",
"refuse": "拒绝",
"transferAccounts": "转账",
"refuseReason": "拒绝原因",
"shopReasonPlaceholder": "请输入拒绝原因",
"confirmDelivery": "确认收货",
"orderDeliveryTips": "确定商品收到了吗?",
"agreeRefundDelivery": "同意买家收货",
"refundDeliveryAddress": "退货地址",
"refundVoucher": "申请凭证",
"refundRemark": "退款描述",
"agreeMoney": "退款金额",
"moneyPlaceholder": "请输入退款金额",
"refundAddressPlaceholder": "请输入退货地址",
"expressCompany": "物流公司",
"expressRemark": "物流说明",
"orderInfoEmpty": "暂无数据",
"closeRefund": "关闭售后",
"closeRefundTips": "建议先与用户沟通协商,确认后再关闭售后流程。请谨慎操作,确定要关闭售后吗?"
}

View File

@ -0,0 +1,94 @@
{
"orderNo": "订单编号",
"orderInfo": "订单信息",
"outTradeNo": "交易流水号",
"orderType": "订单类型",
"orderForm": "订单来源",
"takerName": "收货人",
"takerMobile": "收货人手机号",
"takerFullAddress": "收货地址",
"goodsDetail": "商品信息",
"goodsName": "商品名称",
"price": "价格",
"num": "数量",
"goodsMoney": "商品总额",
"preferentialMoney": "优惠金额",
"deliveryMoney": "配送金额",
"operateLog": "订单日志",
"orderNoPlaceholder": "请输入订单编号",
"orderStatus": "订单状态",
"orderStatusPlaceholder": "请选择订单状态",
"orderFrom": "订单类型",
"orderFromPlaceholder": "请选择订单类型",
"payTime": "支付时间",
"orderGoods": "商品",
"goodsPriceNumber": "单价(元)/数量",
"orderMoney": "实付金额(元)",
"buyInfo": "买家/收货人",
"deliveryType": "配送方式",
"startDate": "开始时间",
"endDate": "结束时间",
"piece": "件",
"payType": "支付方式",
"notes": "备注",
"editAddress": "修改地址",
"remind": "提醒",
"remindTips1": "如果未发货,请点击同意退款给买家。",
"remindTips2": "如果实际已发货,请主动与买家联系。",
"remindTips3": "如果订单整体退款后,优惠券和余额会退还给买家。",
"close": "关闭订单",
"finish": "确认收货",
"delivery": "订单发货",
"deliveryTypePlaceholder": "请选择配送方式",
"company": "物流公司",
"companyPlaceholder": "请选择物流公司",
"expressNumber": "物流单号",
"expressNumberPlaceholder": "请输入物流单号",
"orderGoodsIdsPlaceholder": "请选择订单项",
"virtualDelivery": "虚拟发货",
"orderCloseTips": "关闭订单后该订单将无法支付,是否确认关闭?",
"orderFinishTips": "是否确认用户已经收货?",
"orderGoodsPlaceholder": "请选择要发货的商品",
"memberRemark": "买家留言",
"discountMoney": "订单详情",
"orderDelivery": "物流信息",
"devliveryTime": "发货时间",
"companyName": "物流公司",
"logisticNo": "物流单号",
"packageInfo": "物流包裹信息",
"deliveryInfo": "发货信息",
"goodsInfo": "商品信息",
"logisticInfo": "物流信息",
"storeName": "自提点名称",
"storeAddress": "自提点地址",
"storeMobile": "自提点电话",
"tradeTime": "营业时间",
"deliveryStatusName": "发货状态",
"orderRefundNo": "退款编号",
"createTime": "申请时间",
"refundMoney": "退款金额",
"refundReason": "退款原因",
"afterSales": "售后信息",
"refundStatus": "退款状态",
"orderRefundRefuse": "退款拒绝",
"orderRefundAgree": "同意退款",
"agree": "同意",
"applyMoney": "申请金额",
"refuse": "拒绝",
"transferAccounts": "转账",
"refuseReason": "拒绝原因",
"shopReasonPlaceholder": "请输入拒绝原因",
"confirmDelivery": "确认收货",
"orderDeliveryTips": "确定商品收到了吗?",
"agreeRefundDelivery": "同意买家收货",
"refundDeliveryAddress": "退货地址",
"refundVoucher": "申请凭证",
"refundRemark": "退款描述",
"agreeMoney": "退款金额",
"refundType": "退款方式",
"moneyPlaceholder": "请输入退款金额",
"refundAddressPlaceholder": "请输入退货地址",
"expressCompany": "物流公司",
"expressRemark": "物流说明",
"orderInfoEmpty": "暂无数据"
}

View File

@ -0,0 +1,37 @@
{
"goodsOverview": "商品概况",
"timeFilter": "时间筛选",
"startTime": "开始时间",
"endTime": "结束时间",
"search": "搜索",
"goodsAccessNum": "商品浏览量",
"goodsAccessNumTip": "统计时间内,所有商品详情页被访问的次数,一个人在统计时间内访问多次记为多次",
"goodsVisitCount": "商品访客数",
"goodsVisitCountTips": "统计时间内,访问任何商品详情页的人数,一个人在统计时间范围内访问多次只记为一个",
"cartNum": "加购件数",
"cartNumTips": "统计时间内,添加商品进入购物车的商品件数",
"saleNum": "下单件数",
"saleNumTips": "统计时间内,成功下单的商品件数之和(不剔除退款订单)",
"payNum": "支付件数",
"payNumTips": "统计时间内, 成功付款订单的商品件数之和(不剔除退款订单)",
"payMoney": "支付金额",
"payMoneyTips": "统计时间内,成功付款订单的商品金额之和(不剔除退款订单)",
"refundMoney": "退款金额",
"refundMoneyTips": "统计时间内,成功退款的商品金额之和",
"refundNum": "退款件数",
"refundNumTips": "统计时间内,成功退款的商品件数之和",
"goodsRank": "商品排行",
"goodsName": "商品名称",
"goodsNamePlaceholder": "请输入商品名称",
"goodsCategory": "商品分类",
"all": "全部",
"totalType": "统计类型",
"totalTypePlaceholder": "请选择统计类型",
"goodsInfo": "商品信息",
"accessNum": "访问次数",
"visitCount": "访客数",
"cartNumber": "加入购物车数量",
"saleNumber": "商品销量",
"payTotal": "支付总金额",
"collectNum": "收藏数量"
}

View File

@ -0,0 +1,432 @@
<template>
<div class="main-container">
<el-card class="card !border-none mb-[15px]" shadow="never">
<el-page-header :content="pageName" :icon="ArrowLeft" @back="back" />
</el-card>
<el-card class="box-card !border-none" shadow="never" v-loading="loading">
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form">
<el-form-item :label="t('addressType')" prop="address_type">
<div class="flex flex-col">
<div>
<el-checkbox v-model="formData.is_delivery_address" :label="t('deliveryAddress')" :true-label="1" :false-label="0"/>
<el-checkbox v-model="formData.is_default_delivery" :label="t('defaultDeliveryAddress')" :true-label="1" :false-label="0" v-show="formData.is_delivery_address"/>
</div>
<div>
<el-checkbox v-model="formData.is_refund_address" :label="t('refundAddress')" :true-label="1" :false-label="0"/>
<el-checkbox v-model="formData.is_default_refund" :label="t('defaultRefundAddress')" :true-label="1" :false-label="0" v-show="formData.is_refund_address"/>
</div>
</div>
</el-form-item>
<el-form-item :label="t('contactName')" prop="contact_name">
<el-input v-model.trim="formData.contact_name" clearable :placeholder="t('contactNamePlaceholder')" class="input-width" maxlength="10" />
</el-form-item>
<el-form-item :label="t('mobile')" prop="mobile">
<el-input v-model.trim="formData.mobile" clearable :placeholder="t('mobilePlaceholder')" maxlength="11" class="input-width" @keyup="filterNumber($event)" @blur="formData.mobile = $event.target.value"/>
</el-form-item>
<el-form-item :label="t('fullAddress')" prop="address_area">
<el-select v-model="formData.province_id" value-key="id" clearable class="w-[200px]" ref="provinceRef">
<el-option :label="t('provincePlaceholder')" :value="0"/>
<el-option v-for="(item, index) in areaList.province" :key="index" :label="item.name" :value="item.id"/>
</el-select>
<el-select v-model="formData.city_id" value-key="id" clearable class="w-[200px] ml-3" ref="cityRef">
<el-option :label="t('cityPlaceholder')" :value="0"/>
<el-option v-for="(item, index) in areaList.city " :key="index" :label="item.name" :value="item.id"/>
</el-select>
<el-select v-model="formData.district_id" value-key="id" clearable class="w-[200px] ml-3" ref="districtRef">
<el-option :label="t('districtPlaceholder')" :value="0"/>
<el-option v-for="(item, index) in areaList.district " :key="index" :label="item.name" :value="item.id"/>
</el-select>
</el-form-item>
<el-form-item prop="address">
<el-input v-model.trim="formData.address" clearable :placeholder="t('addressPlaceholder')" @input="areaChange()" class="input-width"/>
</el-form-item>
<el-form-item>
<div id="container" class="w-[800px] h-[520px] relative" v-loading="mapLoading"></div>
</el-form-item>
</el-form>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer !z-[9999]">
<el-button type="primary" @click="onSave(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, onMounted, watch } from 'vue'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import { ArrowLeft } from "@element-plus/icons-vue"
import { getShopAddressInfo, addShopAddress, editShopAddress } from '@/addon/shop/api/shop_address'
import { getMap, getAreaListByPid, getAreaByCode } from '@/app/api/sys'
import { useRoute } from 'vue-router'
import { createMarker, latLngToAddress, addressToLatLng } from '@/utils/qqmap'
import { filterNumber, debounce } from '@/utils/common'
const route = useRoute()
const id: number = parseInt(route.query.id as string)
const loading = ref(false)
const pageName = route.meta.title
interface areaType{
province: any[],
city: any[],
district: any[]
}
const areaList = reactive<areaType>({
province: [],
city: [],
district: []
})
const provinceRef = ref()
const cityRef = ref()
const districtRef = ref()
/**
* 获取省
*/
getAreaListByPid(0).then(res => {
areaList.province = res.data
})
let mapKey: string = ''
onMounted(() => {
const mapScript = document.createElement('script')
getMap().then(res => {
mapKey = res.data.key
mapScript.type = 'text/javascript'
mapScript.src = 'https://map.qq.com/api/gljs?libraries=tools,service&v=1.exp&key=' + res.data.key
document.body.appendChild(mapScript)
})
mapScript.onload = () => {
setTimeout(() => {
initMap()
}, 500)
}
})
/**
* 初始化地图
*/
let map: any
let marker: any
const mapLoading = ref(true)
const initMap = () => {
const TMap = (window as any).TMap
const LatLng = TMap.LatLng
const center = new LatLng(formData.lat, formData.lng)
map = new TMap.Map('container', {
center,
zoom: 14
})
map.on('tilesloaded', () => {
mapLoading.value = false
})
marker = createMarker(map)
map.on('click', (evt: any) => {
map.setCenter(evt.latLng)
marker.updateGeometries({
id: 'center',
position: evt.latLng
})
latLngChange(evt.latLng.lat, evt.latLng.lng)
})
latLngChange(center.lat, center.lng)
}
const storeArea = reactive({
province_id: 0,
city_id: 0,
district_id: 0
})
const latLngChange = (lat: number, lng: number) => {
latLngToAddress({ mapKey, lat, lng }).then(({ message, result }) => {
if (message == 'query ok' || message == 'Success') {
formData.lat = result.location.lat
formData.lng = result.location.lng
formData.address = result.formatted_addresses.recommend
getAreaByCode(result.ad_info.adcode).then(({ data }) => {
storeArea.province_id = data.province ? data.province.id : 0
storeArea.city_id = data.city ? data.city.id : 0
storeArea.district_id = data.district ? data.district.id : 0
})
} else {
console.error(message, result)
}
}).catch(err => {
console.log(err)
})
}
/**
* 表单数据
*/
const initialFormData = {
id: 0,
contact_name: '',
mobile: '',
province_id: 0,
city_id: 0,
district_id: 0,
address: '',
full_address: '',
lat: 39.908626,
lng: 116.397190,
is_delivery_address: 0,
is_refund_address: 0,
is_default_delivery: 0,
is_default_refund: 0
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const setFormData = async (id: number = 0) => {
loading.value = true
Object.assign(formData, initialFormData)
const data = await (await getShopAddressInfo(id)).data
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
loading.value = false
}
if (id) setFormData(id)
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
address_type: [
{
validator: (rule: any, value: any, callback: any) => {
if (!formData.is_delivery_address && !formData.is_refund_address) {
callback(new Error(t('addressTypeRequire')))
}
callback()
}
}
],
contact_name: [
{ required: true, message: t('contactNamePlaceholder'), trigger: 'blur' }
],
mobile: [
{ required: true, message: t('mobilePlaceholder'), trigger: 'blur' },
{
trigger: 'blur',
validator: (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('mobileTips')))
}
callback()
}
}
],
address_area: [
{
validator: (rule: any, value: any, callback: any) => {
if (!formData.province_id) {
callback(new Error(t('provincePlaceholder')))
}
if (!formData.city_id) {
callback(new Error(t('cityPlaceholder')))
}
if (areaList.district.length && !formData.district_id) {
callback(new Error(t('districtPlaceholder')))
}
callback()
}
}
],
address: [
{ required: true, message: t('addressPlaceholder'), trigger: 'blur' }
]
}
})
/**
* 获取市
*/
watch(() => formData.province_id, (nval) => {
if (nval) {
getAreaListByPid(formData.province_id).then(res => {
areaList.city = res.data
const cityId = formData.city_id
if (cityId) {
let isExist = false
for (let i = 0; i < res.data.length; i++) {
if (cityId == res.data[i].id) {
isExist = true
break
}
}
if (isExist) {
formData.city_id = cityId
return
}
}
formData.city_id = 0
areaChange()
})
} else {
formData.city_id = 0
}
})
/**
* 获取区
*/
watch(() => formData.city_id, (nval) => {
if (nval) {
getAreaListByPid(formData.city_id).then(res => {
areaList.district = res.data
const districtId = formData.district_id
if (districtId) {
let isExist = false
for (let i = 0; i < res.data.length; i++) {
if (districtId == res.data[i].id) {
isExist = true
break
}
}
if (isExist) {
formData.district_id = districtId
return
}
}
areaChange()
formData.district_id = 0
})
} else {
formData.district_id = 0
}
})
watch(() => formData.district_id, (nval) => {
if (nval) {
areaChange()
}
})
const areaChange = debounce(() => {
setTimeout(() => {
// let province = areaList.province.map((item) => { if (item.id == formData.province_id) { return item.name } })
// let city = areaList.city.map((item) => { if (item.id == formData.city_id) { return item.name } })
// let district = areaList.district.map((item) => { if (item.id == formData.district_id) { return item.name } })
// idname
let province = areaList.province.find(item => item.id == formData.province_id)?.name || '';
//
let city = areaList.city.find(item => item.id == formData.city_id)?.name || '';
//
let district = areaList.district.find(item => item.id == formData.district_id)?.name || '';
const address = [
formData.province_id ? (provinceRef.value.selectedLabel || province) : '',
formData.city_id ? (cityRef.value.selectedLabel || city) : '',
formData.district_id ? (districtRef.value.selectedLabel || district) : '',
formData.address
]
addressToLatLng({ mapKey, address: address.join('') }).then(({ message, result }) => {
if (message == 'Success' || message == 'query ok') {
const latLng = new (window as any).TMap.LatLng(result.location.lat, result.location.lng)
map.setCenter(latLng)
marker.updateGeometries({
id: 'center',
position: latLng
})
formData.lat = result.location.lat
formData.lng = result.location.lng
} else {
console.error(message, result)
}
})
}, 500)
}, 500)
/**
* 地图点选获取市
*/
watch(() => storeArea.province_id, (nval) => {
if (nval) {
getAreaListByPid(storeArea.province_id).then(res => {
areaList.city = res.data
formData.province_id = storeArea.province_id
formData.city_id = storeArea.city_id
})
}
})
/**
* 地图点选获取区
*/
watch(() => storeArea.city_id, (nval) => {
if (nval) {
getAreaListByPid(storeArea.city_id).then(res => {
areaList.district = res.data
formData.city_id = storeArea.city_id
formData.district_id = storeArea.district_id
})
}
})
/**
* 地图点选获取区
*/
watch(() => storeArea.district_id, (nval) => {
if (nval) {
formData.district_id = storeArea.district_id
}
})
const onSave = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
const data = formData
let province = areaList.province.find(item => item.id == formData.province_id)?.name || '';
//
let city = areaList.city.find(item => item.id == formData.city_id)?.name || '';
//
let district = areaList.district.find(item => item.id == formData.district_id)?.name || '';
const address = [
data.province_id ? (provinceRef.value.selectedLabel || province) : '',
data.city_id ? (cityRef.value.selectedLabel || city) : '',
data.district_id ? (districtRef.value.selectedLabel || district) : '',
data.address
]
data.full_address = address.join('')
const save = id ? editShopAddress : addShopAddress
save(data).then(res => {
loading.value = false
history.back()
}).catch(() => {
loading.value = false
})
}
})
}
const back = () => {
history.back()
}
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,157 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="text-page-title">{{ pageName }}</span>
<el-button type="primary" @click="addEvent">{{ t('addShopAddress') }}</el-button>
</div>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="shopAddressTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('mobile')" prop="mobile">
<el-input v-model.trim="shopAddressTable.searchParam.mobile" :placeholder="t('mobilePlaceholder')" />
</el-form-item>
<el-form-item :label="t('fullAddress')" prop="full_address">
<el-input v-model.trim="shopAddressTable.searchParam.full_address" :placeholder="t('fullAddressPlaceholder')" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadShopAddressList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="mt-[10px]">
<el-table :data="shopAddressTable.data" size="large" v-loading="shopAddressTable.loading">
<template #empty>
<span>{{ !shopAddressTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column prop="contact_name" :label="t('contactName')" min-width="120" />
<el-table-column prop="mobile" :label="t('mobile')" min-width="120" />
<el-table-column prop="full_address" :label="t('fullAddress')" min-width="120" :show-overflow-tooltip="true" />
<el-table-column prop="is_delivery_address" :label="t('addressType')" min-width="120" align="left">
<template #default="{ row }">
<div v-if="row.is_delivery_address">
{{ t('deliveryAddress') }}
<el-tag size="small" v-if="row.is_default_delivery">{{ t('default') }}</el-tag>
</div>
<div v-if="row.is_refund_address">
{{ t('refundAddress') }}
<el-tag size="small" v-if="row.is_default_refund">{{ t('default') }}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column :label="t('operation')" fixed="right" min-width="120" align="right">
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination v-model:current-page="shopAddressTable.page"
v-model:page-size="shopAddressTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="shopAddressTable.total"
@size-change="loadShopAddressList()" @current-change="loadShopAddressList" />
</div>
</div>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import { getShopAddressList, deleteShopAddress } from '@/addon/shop/api/shop_address'
import { ElMessageBox, FormInstance } from 'element-plus'
import { useRouter, useRoute } from 'vue-router'
import { setTablePageStorage, getTablePageStorage } from '@/utils/common'
const route = useRoute()
const pageName = route.meta.title
const shopAddressTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {
mobile: '',
full_address: ''
}
})
const searchFormRef = ref<FormInstance>()
/**
* 获取商家地址库列表
*/
const loadShopAddressList = (page: number = 1) => {
shopAddressTable.loading = true
shopAddressTable.page = page
getShopAddressList({
page: shopAddressTable.page,
limit: shopAddressTable.limit,
...shopAddressTable.searchParam
}).then(res => {
shopAddressTable.loading = false
shopAddressTable.data = res.data.data
shopAddressTable.total = res.data.total
setTablePageStorage(shopAddressTable.page, shopAddressTable.limit, shopAddressTable.searchParam)
}).catch(() => {
shopAddressTable.loading = false
})
}
loadShopAddressList(getTablePageStorage(shopAddressTable.searchParam).page)
const router = useRouter()
/**
* 添加商家地址库
*/
const addEvent = () => {
router.push('/shop/order/address/edit')
}
/**
* 编辑商家地址库
* @param data
*/
const editEvent = (data: any) => {
router.push('/shop/order/address/edit?id=' + data.id)
}
/**
* 删除商家地址库
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('shopAddressDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}
).then(() => {
deleteShopAddress(id).then(() => {
loadShopAddressList(getTablePageStorage(shopAddressTable.searchParam).page)
}).catch(() => {
})
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadShopAddressList()
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,157 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<el-page-header :content="pageName" :icon="ArrowLeft" @back="router.push('/shop/order/delivery')" />
</el-card>
<el-card class="box-card mt-[15px] !border-none" shadow="never">
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<div class="flex justify-between items-center">
<el-form :inline="true" :model="companyTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('companyName')" prop="company_name">
<el-input v-model.trim="companyTable.searchParam.company_name" :placeholder="t('companyNamePlaceholder')" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadCompanyList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
<el-button type="primary" @click="addEvent">{{ t('addCompany') }}</el-button>
</div>
</el-card>
<div class="mt-[10px]">
<el-table :data="companyTable.data" size="large" v-loading="companyTable.loading">
<template #empty>
<span>{{ !companyTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column prop="company_name" :label="t('companyName')" min-width="120" />
<el-table-column prop="logo" :label="t('logo')" min-width="120">
<template #default="{ row }">
<div class="w-[50px] h-[50px] flex items-center justify-center">
<img v-if="row.logo" class="max-w-[100%] max-h-[100%]" :src="img(row.logo)" />
</div>
</template>
</el-table-column>
<el-table-column prop="url" :label="t('url')" min-width="120" />
<el-table-column prop="kd100_express_no" :label="t('expressNoKd100')" min-width="120" />
<el-table-column prop="express_no" :label="t('expressNo')" min-width="120" />
<el-table-column prop="express_no_electronic_sheet" :label="t('expressNoElectronicSheet')" min-width="120" />
<el-table-column :label="t('electronicSheetSwitchName')" min-width="120">
<template #default="{ row }">
<span>{{ row.electronic_sheet_switch == 1 ? '支持' : '不支持' }}</span>
</template>
</el-table-column>
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="120">
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.company_id)">{{ t('delete') }}
</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination v-model:current-page="companyTable.page" v-model:page-size="companyTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="companyTable.total"
@size-change="loadCompanyList()" @current-change="loadCompanyList" />
</div>
</div>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import { getCompanyPageList, deleteCompany } from '@/addon/shop/api/delivery'
import { img, setTablePageStorage, getTablePageStorage } from '@/utils/common'
import { ElMessageBox, FormInstance } from 'element-plus'
import { ArrowLeft } from '@element-plus/icons-vue'
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()
const pageName = route.meta.title
const companyTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {
company_name: '',
logo: '',
url: '',
create_time: '',
modify_time: ''
}
})
const searchFormRef = ref<FormInstance>()
/**
* 获取物流公司列表
*/
const loadCompanyList = (page: number = 1) => {
companyTable.loading = true
companyTable.page = page
getCompanyPageList({
page: companyTable.page,
limit: companyTable.limit,
...companyTable.searchParam
}).then(res => {
companyTable.loading = false
companyTable.data = res.data.data
companyTable.total = res.data.total
setTablePageStorage(companyTable.page, companyTable.limit, companyTable.searchParam);
}).catch(() => {
companyTable.loading = false
})
}
loadCompanyList(getTablePageStorage(companyTable.searchParam).page);
/**
* 添加物流公司
*/
const addEvent = () => {
router.push('/shop/order/delivery/company_add')
}
/**
* 编辑物流公司
* @param data
*/
const editEvent = (data: any) => {
router.push('/shop/order/delivery/company_edit?company_id=' + data.company_id)
}
/**
* 删除物流公司
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('companyDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}
).then(() => {
deleteCompany(id).then(() => {
loadCompanyList(getTablePageStorage(companyTable.searchParam).page);
}).catch(() => {
})
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadCompanyList()
}
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,316 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<el-page-header :content="formData.company_id ? t('updateCompany') : t('addCompany')" :icon="ArrowLeft" @back="back()" />
</el-card>
<el-card class="box-card mt-[15px] !border-none" shadow="never">
<el-form :model="formData" label-width="150px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
<el-form-item :label="t('companyName')" prop="company_name">
<el-input v-model.trim="formData.company_name" maxlength="20" clearable :placeholder="t('companyNamePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('logo')">
<upload-image v-model="formData.logo" />
</el-form-item>
<el-form-item :label="t('url')">
<el-input v-model.trim="formData.url" clearable :placeholder="t('urlPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('expressNoKd100')">
<div>
<el-input v-model.trim="formData.kd100_express_no" clearable :placeholder="t('expressNoKd100Placeholder')" class="input-width" />
<p class="w-[380px] text-[12px] text-[#999] mt-[5px] leading-[20px]">{{ t('expressNoTips') }}</p>
</div>
</el-form-item>
<el-form-item :label="t('expressNo')">
<div>
<el-input v-model.trim="formData.express_no" clearable :placeholder="t('expressNoPlaceholder')" class="input-width" />
<p class="w-[380px] text-[12px] text-[#999] mt-[5px] leading-[20px]">{{ t('expressNoTips') }}</p>
</div>
</el-form-item>
<el-form-item :label="t('expressNoElectronicSheet')">
<div>
<el-input v-model.trim="formData.express_no_electronic_sheet" clearable :placeholder="t('expressNoElectronicSheetPlaceholder')" class="input-width" />
<p class="w-[380px] text-[12px] text-[#999] mt-[5px] leading-[20px]">{{ t('expressNoElectronicSheetTips') }}</p>
</div>
</el-form-item>
<el-form-item :label="t('electronicSheetSwitch')">
<el-switch v-model="formData.electronic_sheet_switch" :active-value="1" :inactive-value="0" />
</el-form-item>
<el-form-item :label="t('expType')" prop="exp_type" v-show="formData.electronic_sheet_switch">
<div class="w-[600px]">
<el-table :data="formData.exp_type" size="large" v-show="formData.exp_type.length">
<template #empty>
<span>{{ formData.exp_type.length == 0 ? t('emptyData') : '' }}</span>
</template>
<el-table-column prop="name" :label="t('expTypeName')" min-width="200">
<template #default="{ row }">
<el-input v-model.trim="row.text" class="input-width" maxlength="20" clearable show-word-limit />
</template>
</el-table-column>
<el-table-column prop="name" :label="t('expTypeValue')" min-width="120">
<template #default="{ row }">
<el-input v-model.trim="row.value" class="!w-[150px]" maxlength="6" clearable show-word-limit @keyup="filterNumber($event)" />
</template>
</el-table-column>
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="60">
<template #default="{ row,$index }">
<el-button type="primary" link @click="deleteExpTypeValueEvent($index)">
{{ t('delete') }}
</el-button>
</template>
</el-table-column>
</el-table>
<el-button type="primary" plain @click="addExpTypeValueEvent" :class="{'mt-[10px]': formData.exp_type.length}" v-show="formData.exp_type.length < expTypeMaxLength">{{ t('addExpType') }}</el-button>
<div class="text-[12px] text-[#999] mt-[5px] leading-[20px]">
<span>{{ t('expTypeTips') }}</span>
<a class="ml-[3px] text-[var(--el-color-primary)]" target="_blank" href="https://www.yuque.com/kdnjishuzhichi/dfcrg1/hgx758hom5p6wz0l">{{ t('examine') }}</a>
</div>
<p class="text-[12px] text-[#999] mt-[3px] leading-[20px]">{{ t('expTypeTips1') }}</p>
</div>
</el-form-item>
<el-form-item :label="t('printStyle')" prop="print_style" v-show="formData.electronic_sheet_switch">
<div class="w-[600px]">
<el-table :data="formData.print_style" size="large" v-show="formData.print_style.length">
<template #empty>
<span>{{ formData.print_style.length == 0 ? t('emptyData') : '' }}</span>
</template>
<el-table-column prop="name" :label="t('printStyleName')" min-width="200">
<template #default="{ row }">
<el-input v-model.trim="row.template_name" class="input-width" maxlength="20" clearable show-word-limit />
</template>
</el-table-column>
<el-table-column prop="name" :label="t('printStyleId')" min-width="120">
<template #default="{ row }">
<el-input v-model.trim="row.template_size" class="!w-[150px]" maxlength="6" clearable show-word-limit />
</template>
</el-table-column>
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="60">
<template #default="{ row,$index }">
<el-button type="primary" link @click="deletePrintStyleValueEvent($index)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<el-button type="primary" plain @click="addPrintStyleValueEvent" :class="{'mt-[10px]': formData.print_style.length}" v-show="formData.print_style.length < printStyleMaxLength">{{ t('addPrintStyle') }}</el-button>
<div class="text-[12px] text-[#999] mt-[5px] leading-[20px]">
<span>{{ t('printStyleTips') }}</span>
<a class="ml-[3px] text-[var(--el-color-primary)]" target="_blank" href="https://www.yuque.com/kdnjishuzhichi/dfcrg1/vpptucr1q5ahcxa7">{{ t('examine') }}</a>
</div>
<p class="text-[12px] text-[#999] mt-[3px] leading-[20px]">{{ t('printStyleTips1') }}</p>
<p class="text-[12px] text-[#999] mt-[3px] leading-[20px]">{{ t('printStyleTips2') }}</p>
</div>
</el-form-item>
</el-form>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="save(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('back') }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref, computed } from 'vue'
import { t } from '@/lang'
import { addCompany, editCompany, getCompanyInfo } from '@/addon/shop/api/delivery'
import { FormInstance } from 'element-plus'
import { ArrowLeft } from '@element-plus/icons-vue'
import { useRoute, useRouter } from 'vue-router'
import { filterNumber } from '@/utils/common'
const route = useRoute()
const router = useRouter()
const loading = ref(true)
const back = () => {
router.push('/shop/order/delivery/company')
}
/**
* 表单数据
*/
const initialFormData = {
company_id: '',
company_name: '',
logo: '',
url: '',
kd100_express_no: '',
express_no: '',
express_no_electronic_sheet: "",
print_style: [],
exp_type: [],
electronic_sheet_switch: 1
}
const printStyleMaxLength = ref(10)
const expTypeMaxLength = ref(10)
const formData: Record<string, any> = reactive({ ...initialFormData })
formData.company_id = ref(route.query.company_id)
const getCompanyInfoFn = () => {
getCompanyInfo(formData.company_id).then(res => {
loading.value = false;
let data = res.data;
if (data) {
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
})
}
if (formData.company_id) {
getCompanyInfoFn();
} else {
loading.value = false;
}
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
company_name: [
{ required: true, message: t('companyNamePlaceholder'), trigger: 'blur' }
],
exp_type: [
{
trigger: 'blur',
validator: (rule: any, value: any, callback: any) => {
if (!value.length) {
callback()
return false;
}
let textArr = []; //
let valArr = []; //
for (let i = 0; i < value.length; i++) {
if (!value[i].text) {
callback(new Error(t('expTypeTextTips')))
break;
} else if (value[i].text) {
textArr.push(value[i].text);
}
if (!value[i].value) {
callback(new Error(t('expTypeValueTips')))
break;
} else if (parseFloat(value[i].value) == 0) {
callback(new Error(t('expTypeValueNullTips')))
break;
} else if (value[i].value) {
valArr.push(value[i].value);
}
}
if (new Set(textArr).size !== textArr.length) {
callback(new Error(t('expTypeTextRepeatTips')))
}
if (new Set(valArr).size !== valArr.length) {
callback(new Error(t('expTypeValueRepeatTips')))
}
callback()
}
}
],
print_style: [
{
trigger: 'blur',
validator: (rule: any, value: any, callback: any) => {
if (!value.length) {
callback()
return false;
}
let nameArr = []; //
let sizeArr = []; //
for (let i = 0; i < value.length; i++) {
if (!value[i].template_name) {
callback(new Error(t('printStyleNameTips')))
break;
} else if (value[i].template_name) {
nameArr.push(value[i].template_name);
}
if (!value[i].template_size) {
callback(new Error(t('printStyleSizeTips')))
break;
} else if (value[i].template_size) {
sizeArr.push(value[i].template_size);
}
}
if (new Set(nameArr).size !== nameArr.length) {
callback(new Error(t('printStyleNameRepeatTips')))
}
if (new Set(sizeArr).size !== sizeArr.length) {
callback(new Error(t('printStyleSizeRepeatTips')))
}
callback()
}
}
]
}
})
//
const addPrintStyleValueEvent = () => {
formData.print_style.push({
template_name: '',
template_size: ''
})
}
//
const addExpTypeValueEvent = () => {
formData.exp_type.push({
text: '',
value: ''
})
}
//
const deletePrintStyleValueEvent = (index: any) => {
formData.print_style.splice(index, 1)
}
//
const deleteExpTypeValueEvent = (index: any) => {
formData.exp_type.splice(index, 1)
}
/**
* 确认
* @param formEl
*/
const repeat = ref(false)
const save = async(formEl: FormInstance | undefined) => {
if (repeat.value || !formEl) return
const api = formData.company_id ? editCompany : addCompany
await formEl.validate(async(valid) => {
if (valid) {
repeat.value = true
const data = formData
api(data).then(res => {
router.push('/shop/order/delivery/company')
repeat.value = false
}).catch(() => {
repeat.value = false
})
}
})
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,111 @@
<template>
<el-dialog v-model="showDialog" :title="formData.deliver_id ? t('updateDeliver') : t('addDeliveryPersonnel')" width="480" class="diy-dialog-wrap" :destroy-on-close="true">
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
<el-form-item :label="t('deliverName')" prop="deliver_name">
<el-input v-model.trim="formData.deliver_name" clearable :placeholder="t('deliverNamePlaceholder')" class="input-width" maxlength="10" />
</el-form-item>
<el-form-item :label="t('deliverMobile')" prop="deliver_mobile">
<el-input v-model.trim="formData.deliver_mobile" clearable :placeholder="t('deliverMobilePlaceholder')" class="input-width" @keyup="filterNumber($event)" @blur="formData.deliver_mobile = $event.target.value" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" :loading="loading" @click="confirm(formRef)">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, reactive, computed } from 'vue'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import { addShopDeliver, editShopDeliver, getShopDeliverInfo } from '@/addon/shop/api/delivery'
import { filterNumber } from '@/utils/common'
const showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
deliver_id: '',
deliver_name: '',
deliver_mobile: ''
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
deliver_name: [
{ required: true, message: t('deliverNamePlaceholder'), trigger: 'blur' }
],
deliver_mobile: [
{ required: true, message: t('deliverMobilePlaceholder'), trigger: 'blur' },
{ min: 11, max: 11, message: '请输入11位手机号码', trigger: 'blur' },
{
pattern: /^1[23456789]\d{9}$/,
message: '请输入正确的手机号码',
trigger: 'blur'
}
]
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async(formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
const save = formData.deliver_id ? editShopDeliver : addShopDeliver
await formEl.validate(async(valid) => {
if (valid) {
loading.value = true
const data = formData
save(data).then(res => {
loading.value = false
showDialog.value = false
emit('complete')
}).catch(() => {
loading.value = false
})
}
})
}
const setFormData = async(row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if (row) {
const data = await (await getShopDeliverInfo(row.deliver_id)).data
if (data) {
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
}
loading.value = false
}
defineExpose({
showDialog,
setFormData
})
</script>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label {
height: auto !important;
}
</style>

View File

@ -0,0 +1,137 @@
<template>
<div class="main-container" v-loading="loading">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center mb-[20px]">
<span class="text-page-title">{{ pageName }}</span>
</div>
<div ref="tableRef" :key="toggleIndex" v-if="!loading">
<template v-for="(item, index) in tableData" :key="item.key">
<div class="mb-[20px] bg-[#fff]">
<el-card shadow="never">
<template #header>
<div class="flex items-center justify-between">
<div class="flex items-center">
<i class="iconfont icontuodong vues-rank mr-[5px]"></i>
<el-input v-focus v-if="index === activeIndex" v-model.trim="inputValue" class="w-[120px]" maxlength="10" @blur="inputBlur" />
<span v-else class="font-600 text-[14px]">{{ item.name }}</span>
<el-icon class="text-color ml-[10px] cursor-pointer" @click="edit(index)">
<EditPen />
</el-icon>
</div>
<el-switch v-model="item.status" :active-value="1" :inactive-value="2" @change="update" />
</div>
</template>
<div class="flex items-center justify-between">
<span class="text-[#666666] text-[14px]">{{ t(item.key) }}</span>
<div>
<template v-if="item.key === 'local_delivery'">
<el-button type="primary" link @click="goRouter('/shop/order/delivery/staff')">{{ t('deliveryStaff') }}</el-button>
<el-button type="primary" link @click="goRouter('/shop/order/delivery/local')">{{ t('localConfig') }}</el-button>
</template>
<template v-if="item.key === 'express'">
<el-button type="primary" link @click="goRouter('/shop/order/delivery/company')">{{ t('deliveryCompany') }}</el-button>
<el-button type="primary" link @click="goRouter('/shop/order/shipping/template')">{{ t('deliveryTemplate') }}</el-button>
<el-button type="primary" link @click="goRouter('/shop/order/delivery/search')">{{ t('deliverySearch') }}</el-button>
</template>
<template v-if="item.key === 'store'">
<el-button type="primary" link @click="goRouter('/shop/order/delivery/store')">{{ t('deliveryStore') }}</el-button>
</template>
</div>
</div>
</el-card>
</div>
</template>
</div>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { onMounted, nextTick, ref, toRaw } from 'vue'
import { t } from '@/lang'
import Sortable from 'sortablejs'
import { getShopDeliveryList, setShopDeliveryConfig } from '@/addon/shop/api/delivery'
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()
const pageName = route.meta.title
const loading = ref(false)
interface TableDataType {
key: string
name: string
status: number
}
const tableData = ref<TableDataType[]>([])
const getShopDeliveryListFn = () => {
loading.value = true
getShopDeliveryList().then(res => {
tableData.value = res.data
loading.value = false
nextTick(() => {
if (rowDrop) rowDrop()
})
}).catch(() => {
loading.value = false
})
}
onMounted(() => {
getShopDeliveryListFn()
})
//
const toggleIndex = ref(0)
const tableRef = ref()
const rowDrop = () => {
Sortable.create(tableRef.value, {
handle: '.vues-rank',
animation: 300,
onEnd({ newIndex, oldIndex }) {
const currRow = tableData.value.splice(oldIndex, 1)[0]
tableData.value.splice(newIndex, 0, currRow)
toggleIndex.value += 1
nextTick(() => {
rowDrop()
})
update()
}
})
}
//
const activeIndex = ref<number | null>(null)
const inputValue = ref('')
const edit = (index: number) => {
activeIndex.value = index
inputValue.value = toRaw(tableData.value[index].name)
}
const inputBlur = () => {
if (inputValue.value == '' || tableData.value[activeIndex.value].name === inputValue.value) {
activeIndex.value = null
inputValue.value = ''
return false
}
tableData.value[activeIndex.value].name = inputValue.value
activeIndex.value = null
update()
}
const update = () => {
setShopDeliveryConfig({
value: tableData.value
})
}
const goRouter = (path: string) => {
router.push({ path })
}
</script>
<style lang="scss" scoped>
.text-color {
color: var(--el-color-primary);
}
:deep(.el-card__header) {
padding-top: 5px !important;
padding-bottom: 5px !important;
}
</style>

View File

@ -0,0 +1,198 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center mb-[5px]">
<span class="text-lg">{{pageName}}</span>
<el-button type="primary" @click="addEvent">{{ t('addElectronicSheet') }}</el-button>
</div>
<el-tabs model-value="/shop/delivery/electronic_sheet" @tab-change="handleClick">
<el-tab-pane :label="t('tabESTemplate')" name="/shop/delivery/electronic_sheet" />
<el-tab-pane :label="t('tabESConfig')" name="/shop/delivery/electronic_sheet/config" />
</el-tabs>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="tableData.searchParam" ref="searchFormRef">
<el-form-item :label="t('templateName')" prop="template_name">
<el-input v-model.trim="tableData.searchParam.template_name" :placeholder="t('templateNamePlaceholder')" maxlength="30" />
</el-form-item>
<el-form-item :label="t('expressCompany')" prop="express_company_id">
<el-select v-model="tableData.searchParam.express_company_id" :placeholder="t('expressCompanyPlaceholder')" clearable>
<el-option v-for="item in companyList" :key="item.company_id" :label="item.company_name" :value="item.company_id" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="mt-[10px]">
<el-table :data="tableData.data" size="large" v-loading="tableData.loading">
<template #empty>
<span>{{ !tableData.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column :label="t('templateName')" min-width="200" :show-overflow-tooltip="true">
<template #default="{ row }">
<el-tag size="small" v-if="row.is_default">{{ t('isDefault') }}</el-tag>
<span class="ml-[8px]">{{row.template_name}}</span>
</template>
</el-table-column>
<el-table-column prop="express_company_id" :label="t('expressCompany')" min-width="120" :show-overflow-tooltip="true">
<template #default="{ row }">
<div>{{ row.company.company_name }}</div>
</template>
</el-table-column>
<el-table-column prop="pay_type_name" :label="t('payType')" min-width="80" :show-overflow-tooltip="true"/>
<el-table-column prop="status" :label="t('status')" min-width="80" :show-overflow-tooltip="true" >
<template #default="{ row }">
<div v-if="row.status == 1">{{ t('statusOn') }}</div>
<div v-if="row.status == 0">{{ t('statusOff') }}</div>
</template>
</el-table-column>
<el-table-column :label="t('operation')" fixed="right" min-width="80" align="right">
<template #default="{ row }">
<el-button type="primary" link v-if="!row.is_default" @click="setDefaultEvent(row.id)">{{ t('setDefault') }}</el-button>
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-button type="primary" link v-if="!row.is_default" @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.limit"
layout="total, sizes, prev, pager, next, jumper" :total="tableData.total"
@size-change="loadList()" @current-change="loadList" />
</div>
</div>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import { getElectronicSheetPageList, deleteElectronicSheet, setDefaultElectronicSheet } from '@/addon/shop/api/electronic_sheet'
import { ElMessageBox, FormInstance } from 'element-plus'
import { useRoute, useRouter } from 'vue-router'
import { getCompanyList } from '@/addon/shop/api/delivery'
import { setTablePageStorage, getTablePageStorage } from '@/utils/common'
const route = useRoute()
const router = useRouter()
const pageName = route.meta.title
const tableData = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {
template_name: '',
express_company_id: ''
}
})
const searchFormRef = ref<FormInstance>()
const handleClick = (path: string) => {
router.push({ path })
}
/**
* 获取电子面单列表
*/
const loadList = (page: number = 1) => {
tableData.loading = true
tableData.page = page
getElectronicSheetPageList({
page: tableData.page,
limit: tableData.limit,
...tableData.searchParam
}).then(res => {
tableData.loading = false
tableData.data = res.data.data
tableData.total = res.data.total
setTablePageStorage(tableData.page, tableData.limit, tableData.searchParam)
}).catch(() => {
tableData.loading = false
})
}
loadList(getTablePageStorage(tableData.searchParam).page)
const companyList = ref([])
getCompanyList({
electronic_sheet_switch: 1
}).then((res:any) => {
companyList.value = res.data
})
/**
* 添加电子面单
*/
const addEvent = () => {
router.push('/shop/delivery/electronic_sheet_add')
}
/**
* 编辑电子面单
* @param data
*/
const editEvent = (data: any) => {
router.push('/shop/delivery/electronic_sheet_edit?id=' + data.id)
}
/**
* 删除电子面单
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('electronicSheetDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}
).then(() => {
deleteElectronicSheet(id).then(() => {
loadList(getTablePageStorage(tableData.searchParam).page)
})
})
}
/**
* 设置默认电子面单模版
*/
const setDefaultEvent = (id: number) => {
ElMessageBox.confirm(t('electronicSheetSetDefaultTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}
).then(() => {
setDefaultElectronicSheet({ id }).then(() => {
loadList(getTablePageStorage(tableData.searchParam).page)
})
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadList()
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,185 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center mb-[5px] h-[32px]">
<span class="text-lg">{{ pageName }}</span>
</div>
<el-tabs model-value="/shop/delivery/electronic_sheet/config" @tab-change="handleClick">
<el-tab-pane :label="t('tabESTemplate')" name="/shop/delivery/electronic_sheet" />
<el-tab-pane :label="t('tabESConfig')" name="/shop/delivery/electronic_sheet/config" />
</el-tabs>
<el-form class="page-form" :model="formData" :rules="formRules" label-width="150px" ref="formRef" v-loading="loading">
<el-card class="box-card !border-none" shadow="never">
<h3 class="panel-title !text-sm">{{ t('apiSet') }}</h3>
<el-form-item :label="t('interfaceType')" prop="interface_type">
<div>
<el-radio-group v-model="formData.interface_type">
<el-radio label="kdbird" size="large">{{ t('kdn') }}</el-radio>
</el-radio-group>
<template v-if="formData.interface_type == 'kdbird'">
<p class="text-[12px] text-[#b2b2b2]">
{{ t('promptTips1-1') }}
<el-button class="button-size" type="primary" link @click="kdnEvent('https://www.kdniao.com/reg?from=niucloud')">https://www.kdniao.com</el-button>
</p>
</template>
</div>
</el-form-item>
<div v-if="formData.interface_type == 'kdbird'">
<el-form-item :label="t('kdnEBusinessIDLabel')" class="input-item">
<div>
<el-input v-model.trim="formData.kdniao_id" :placeholder="t('kdnEBusinessIDPlaceholder')" class="input-width" clearable />
<p class="text-[12px] text-[#b2b2b2]">{{ t('kdnEBusinessIDTips') }}</p>
</div>
</el-form-item>
<el-form-item label="API key" class="input-item">
<div>
<el-input v-model.trim="formData.kdniao_api_key" clearable :placeholder="t('kdnAppKeyPlaceholder')" class="input-width" />
<p class="text-[12px] text-[#b2b2b2]">{{ t('kdnAppKeyTips') }}</p>
</div>
</el-form-item>
</div>
</el-card>
<el-card class="box-card !border-none" shadow="never">
<h3 class="panel-title !text-sm">{{ t('printerSet') }}</h3>
<el-alert type="warning" :closable="false" class="!mb-[10px]">
<template #default>
<p>用双端口加载主JS文件Lodop.js或CLodopfuncs.js兼容老版本以防其中某端口被占</p>
<p>HTTP推荐端口8000/18000HTTPS推荐端口8443</p>
<p>1. 请将打印机连接至本机 </p>
<p>2. 在本机上安装打印控件下载链接<a href="http://www.lodop.net/download.html" target="_blank" class="text-primary">http://www.lodop.net/download.html</a></p>
<p>3. 将打印控件中的打印端口下面的打印端口设为相同</p>
</template>
</el-alert>
<el-form-item :label="t('serverPort1')" class="input-item-required" prop="server_port1">
<div>
<el-input v-model.trim="formData.server_port1" :placeholder="t('serverPort1Placeholder')" class="input-width" clearable />
</div>
</el-form-item>
<el-form-item :label="t('serverPort2')" class="input-item-required" prop="server_port2">
<div>
<el-input v-model.trim="formData.server_port2" :placeholder="t('serverPort2Placeholder')" class="input-width" clearable />
</div>
</el-form-item>
<el-form-item :label="t('httpsPort')" class="input-item-required" prop="https_port">
<div>
<el-input v-model.trim="formData.https_port" :placeholder="t('httpsPortPlaceholder')" class="input-width" clearable />
</div>
</el-form-item>
</el-card>
</el-form>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" :loading="loading" @click="save(formRef)">{{ t('save') }}</el-button>
</div>
</div>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import { FormInstance, FormRules } from 'element-plus'
import { useRoute, useRouter } from 'vue-router'
import { setElectronicSheetConfig, getElectronicSheetConfig } from '@/addon/shop/api/electronic_sheet'
const route = useRoute()
const router = useRouter()
const pageName = route.meta.title;
const loading = ref(true)
const handleClick = (path: string) => {
router.push({ path })
}
const formData: any = reactive({
interface_type: 'kdbird',
kdniao_id: '',
kdniao_api_key: '',
server_port1: '8000',
server_port2: '18000',
https_port: '8443'
})
const setFormData = async() => {
const data = await (await getElectronicSheetConfig()).data
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
loading.value = false
}
setFormData()
const kdnEvent = (url: any) => {
window.open(url, '_blank')
}
const formRef = ref<FormInstance>()
//
const formRules = reactive<FormRules>({
server_port1: [
{ required: true, message: t('serverPort1Placeholder'), trigger: 'blur' },
],
server_port2: [
{ required: true, message: t('serverPort2Placeholder'), trigger: 'blur' },
],
https_port: [
{ required: true, message: t('httpsPortPlaceholder'), trigger: 'blur' },
]
})
/**
* 保存
*/
const save = async(formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async(valid) => {
if (valid) {
loading.value = true
setElectronicSheetConfig(formData).then(() => {
loading.value = false
}).catch(() => {
loading.value = false
})
}
})
}
</script>
<style lang="scss" scoped>
.input-item {
margin-bottom: 10px !important
}
.input-item-required {
margin-bottom: 20px !important
}
.button-size {
font-size: 12px !important;
}
.el-radio.el-radio--large {
height: auto !important
}
</style>

View File

@ -0,0 +1,270 @@
<template>
<div class="main-container">
<el-card class="card !border-none mb-[15px]" shadow="never">
<el-page-header :content="pageName" :icon="ArrowLeft" @back="back" />
</el-card>
<el-form class="page-form" :model="formData" :rules="formRules" label-width="150px" ref="formRef" v-loading="loading">
<el-card class="box-card !border-none" shadow="never">
<h3 class="panel-title !text-sm">{{ t('basicSettings') }}</h3>
<el-form-item :label="t('templateName')" prop="template_name">
<el-input v-model.trim="formData.template_name" clearable :placeholder="t('templateNamePlaceholder')" class="input-width" maxlength="30" />
</el-form-item>
<el-form-item :label="t('expressCompany')" prop="express_company_id">
<el-select v-model="formData.express_company_id" :placeholder="t('expressCompanyPlaceholder')" clearable @change="handleSelectCompanyChange">
<el-option v-for="item in companyList" :key="item.company_id" :label="item.company_name" :value="item.company_id" />
</el-select>
</el-form-item>
<el-form-item :label="t('expType')" prop="exp_type" v-show="expTypeList.length">
<el-radio-group v-model="formData.exp_type">
<el-radio v-for="(item,index) in expTypeList" :key="index" :value="item.value">{{ item.text }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('printStyle')" v-show="printStyleList.length">
<div>
<el-select v-model="formData.print_style" :placeholder="t('printStylePlaceholder')" clearable>
<el-option v-for="(item,index) in printStyleList" :key="index" :label="item.template_name" :value="item.template_size" />
</el-select>
<div class="text-[12px] text-[#999] mt-[3px] leading-[20px]">{{ t('printStyleTips1') }}</div>
<div class="text-[12px] text-[#999] mt-[3px] leading-[20px]">{{ t('printStyleTips2') }}</div>
</div>
</el-form-item>
</el-card>
<el-card class="box-card !border-none" shadow="never">
<h3 class="panel-title !text-sm">{{ t('otherSettings') }}</h3>
<el-form-item :label="t('customerName')">
<div>
<el-input v-model.trim="formData.customer_name" clearable class="input-width" maxlength="20" />
<div class="flex items-center mt-[5px] text-[12px] text-[#999] leading-[20px]">
<span>{{ t('customerNameTips') }}</span>
<a class="ml-[3px] text-[var(--el-color-primary)]" target="_blank" href="https://www.yuque.com/kdnjishuzhichi/rg4owd">{{ t('examine') }}</a>
</div>
<div class="flex items-center mt-[3px] text-[12px] text-[#999] leading-[20px]">
<span>{{ t('customerNameTips1') }}</span>
<a class="ml-[3px] text-[var(--el-color-primary)]" target="_blank" href="https://www.yuque.com/kdnjishuzhichi/dfcrg1/hrfw43">{{ t('examine') }}</a>
</div>
</div>
</el-form-item>
<el-form-item :label="t('customerPwd')">
<div>
<el-input v-model.trim="formData.customer_pwd" clearable class="input-width" maxlength="50" />
<div class="mt-[5px] text-[12px] text-[#999] leading-[20px]">{{ t('customerPwdTips') }}</div>
</div>
</el-form-item>
<el-form-item :label="t('sendSite')">
<div>
<el-input v-model.trim="formData.send_site" clearable class="input-width" maxlength="20" />
<div class="mt-[5px] text-[12px] text-[#999] leading-[20px]">{{ t('sendSiteTips') }}</div>
</div>
</el-form-item>
<el-form-item :label="t('sendStaff')">
<div>
<el-input v-model.trim="formData.send_staff" clearable class="input-width" maxlength="20" />
<div class="mt-[5px] text-[12px] text-[#999] leading-[20px]">{{ t('sendStaffTips') }}</div>
</div>
</el-form-item>
<el-form-item :label="t('monthCode')">
<div>
<el-input v-model.trim="formData.month_code" clearable class="input-width" maxlength="20" />
<div class="mt-[5px] text-[12px] text-[#999] leading-[20px]">{{ t('monthCodeTips') }}</div>
</div>
</el-form-item>
<el-form-item :label="t('payType')">
<el-radio-group v-model="formData.pay_type">
<el-radio v-for="(item,index) in payType" :value="parseInt(index)">{{ item }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('isNotice')">
<div>
<el-radio-group v-model="formData.is_notice">
<el-radio :value="1">{{ t('yes') }}</el-radio>
<el-radio :value="0">{{ t('no') }}</el-radio>
</el-radio-group>
<div class="mt-[5px] text-[12px] text-[#999] leading-[20px]">{{ t('isNoticeTips') }}</div>
</div>
</el-form-item>
<el-form-item :label="t('status')">
<el-switch v-model="formData.status" :active-value="1" :inactive-value="0" />
</el-form-item>
<el-form-item :label="t('isDefault')">
<el-switch v-model="formData.is_default" :active-value="1" :inactive-value="0" />
</el-form-item>
</el-card>
</el-form>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" :loading="repeat" @click="confirm(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, computed } from 'vue'
import { t } from '@/lang'
import { ArrowLeft } from "@element-plus/icons-vue"
import type { FormInstance } from 'element-plus'
import { useRoute, useRouter } from 'vue-router'
import {
addElectronicSheet,
editElectronicSheet,
getElectronicSheetInfo,
getElectronicSheetPayType
} from '@/addon/shop/api/electronic_sheet'
import { getCompanyList } from '@/addon/shop/api/delivery'
const loading = ref(false)
const route = useRoute()
const router = useRouter()
const repeat = ref(false)
const pageName = route.meta.title
/**
* 表单数据
*/
const initialFormData: any = {
id: route.query.id || 0,
template_name: '',
express_company_id: '',
customer_name: '',
customer_pwd: '',
send_site: '',
send_staff: '',
month_code: '',
pay_type: 1,
is_notice: 0,
status: 1,
exp_type: 1,
print_style: '',
is_default: ''
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
template_name: [
{ required: true, message: t('templateNamePlaceholder'), trigger: 'blur' },
],
express_company_id: [
{ required: true, message: t('expressCompanyPlaceholder'), trigger: 'blur' },
]
}
})
const companyList: any = ref([]) //
const expTypeList: any = ref([]) //
const printStyleList: any = ref([]) //
const payType = ref([])
const init = async() => {
getElectronicSheetPayType().then((res: any) => {
payType.value = res.data;
})
await getCompanyList({ electronic_sheet_switch: 1 }).then((res: any) => {
companyList.value = res.data;
})
if (formData.id) {
loading.value = true
getElectronicSheetInfo(formData.id).then((res: any) => {
let data = res.data;
if (data) Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
loading.value = false
handleSelectCompanyChange(formData.express_company_id, true)
})
}
}
init();
const handleSelectCompanyChange = (value: any, load: any = false) => {
if (!value) {
expTypeList.value = [];
printStyleList.value = [];
return;
}
for (let i = 0; i < companyList.value.length; i++) {
if (companyList.value[i].company_id == value) {
expTypeList.value = companyList.value[i].exp_type;
expTypeList.value.forEach((item: any) => {
if (item.value) item.value = parseInt(item.value);
})
printStyleList.value = companyList.value[i].print_style;
if (!load) {
if (expTypeList.value.length) {
formData.exp_type = expTypeList.value[0].value
} else {
formData.exp_type = 1; // 1
}
if (printStyleList.value.length) {
formData.print_style = printStyleList.value[0].value
} else {
formData.print_style = ''; //
}
}
break;
}
}
}
/**
* 确认
* @param formEl
*/
const confirm = async(formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id ? editElectronicSheet : addElectronicSheet
await formEl.validate(async(valid) => {
if (valid) {
if (repeat.value) return
repeat.value = true
let data = formData
save(data).then(res => {
repeat.value = false
if (!formData.id) {
router.push('/shop/delivery/electronic_sheet')
}
}).catch(err => {
repeat.value = false
})
}
})
}
const back = () => {
router.push('/shop/delivery/electronic_sheet')
}
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,749 @@
<template>
<div class="main-container">
<el-card class="card !border-none mb-[15px]" shadow="never">
<el-page-header :content="pageName" :icon="ArrowLeft" @back="back" />
</el-card>
<el-card class="box-card !border-none" shadow="never">
<el-form label-width="120px" ref="formRef" :rules="formRules" :model="formData" class="page-form"
v-loading="loading">
<!-- <h3 class="panel-title">{{t('basicSettings')}}</h3> -->
<el-form-item :label="t('deliveryType')" prop="delivery_type">
<!-- <el-checkbox-group v-model="formData.delivery_type">
<el-checkbox label="business">{{ t('business') }}</el-checkbox>
<el-checkbox label="third">{{ t('三方配送') }}</el-checkbox>
</el-checkbox-group> -->
<el-radio-group v-model="formData.delivery_type">
<el-radio :label="'business'">{{ t('business') }}</el-radio>
<el-radio :label="'third'">{{ t('thrid') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('thrid')" prop="time_is_open" v-if="formData.delivery_type=='third'">
<div v-for="(service, index) in thirdPartyData" :key="service.type">
<div class="flex items-center mr-[20px]">
<span class="mr-[10px]">{{ service.name }}</span>
<el-switch v-model="service.isEnabled" @change="handleServiceChange(index)"></el-switch>
</div>
</div>
</el-form-item>
<template v-if="activeService && formData.delivery_type=='third'">
<el-form-item :label="t('AppKey')" prop="app_key">
<el-input v-model="activeService.config.app_key" clearable :placeholder="t('AppKeyRequire')" class="input-width" maxlength="100" show-word-limit />
</el-form-item>
<el-form-item :label="t('AppSecret')" prop="app_secret">
<el-input v-model="activeService.config.app_secret" clearable :placeholder="t('AppSecretRequire')" class="input-width" maxlength="100" show-word-limit />
</el-form-item>
<el-form-item :label="t('shopId')" prop="shop_id">
<el-input v-model="activeService.config.shop_id" clearable :placeholder="t('shopIdRequire')" class="input-width" maxlength="100" show-word-limit />
</el-form-item>
<el-form-item :label="t('shopStoreNo')" prop="shop_store_no">
<el-input v-model="activeService.config.shop_store_no" clearable :placeholder="t('shopStoreNoRequire')" class="input-width" maxlength="100" show-word-limit />
</el-form-item>
</template>
<el-form-item :label="t('timeIsOpen')" prop="time_is_open">
<div>
<el-radio-group v-model="formData.time_is_open">
<el-radio :label="1">{{ t('open') }}</el-radio>
<el-radio :label="0">{{ t('close') }}</el-radio>
</el-radio-group>
<div class="mt-[10px] text-[12px] text-[#999] leading-[20px]">{{t('timeIsOpenTips')}}</div>
</div>
</el-form-item>
<template v-if="formData.time_is_open === 1">
<el-form-item>
<el-radio-group v-model="formData.time_type">
<el-radio :label="0">{{ t('everyDay') }}</el-radio>
<el-radio :label="1">{{ t('custom') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item prop="time_week" v-if="formData.time_type===1">
<el-checkbox-group v-model="formData.time_week">
<el-checkbox label="1">{{ t('monday') }}</el-checkbox>
<el-checkbox label="2">{{ t('tuesday') }}</el-checkbox>
<el-checkbox label="3">{{ t('wednesday') }}</el-checkbox>
<el-checkbox label="4">{{ t('thursday') }}</el-checkbox>
<el-checkbox label="5">{{ t('friday') }}</el-checkbox>
<el-checkbox label="6">{{ t('saturday') }}</el-checkbox>
<el-checkbox label="0">{{ t('sunday') }}</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item :label="t('deliveryTime')" prop="delivery_time">
<div>
<div>
<div v-for="(timeRange, index) in formData.delivery_time" :key="index" class="mb-3">
<el-time-picker v-model="timeRange.start_time" :placeholder="t('startTime')"
format="HH:mm" value-format="HH:mm"
:picker-options="{selectableRange: '00:00 - 23:59'}" />
<span class="mx-2">-</span>
<el-time-picker v-model="timeRange.end_time" :placeholder="t('endTime')" format="HH:mm"
value-format="HH:mm" :picker-options="{selectableRange: '00:00 - 23:59'}" />
<span v-if="index > 0" class="text-primary cursor-pointer ml-[10px]"
@click="removeTimeRange(index)"> {{ t('delete') }}</span>
</div>
<span class="text-primary cursor-pointer mr-[10px]" @click="addTimeRange"
v-if="formData.delivery_time.length < 3"> {{ t('addTime') }}</span>
</div>
<div class="text-[12px] text-[#999]">{{ t('deliveryTimeTips') }}</div>
</div>
</el-form-item>
<el-form-item :label="t('timeInterval')" prop="time_interval">
<div>
<el-radio-group v-model="formData.time_interval">
<el-radio :label="30">{{ t('30minute') }}</el-radio>
<el-radio :label="60">{{ t('oneHour') }}</el-radio>
<el-radio :label="90">{{ t('90minute') }}</el-radio>
<el-radio :label="120">{{ t('twoHour') }}</el-radio>
</el-radio-group>
<!-- <p class="text-[12px] text-[#999]">{{ t('storeTimeIntervalTips') }}</p> -->
</div>
</el-form-item>
<el-form-item :label="t('advancaDay')" prop="advance_day">
<div>
<div class="flex">
{{ t('advance') }}
<div class="w-[100px] mx-[5px]">
<el-input v-model.trim="formData.advance_day" />
</div>
{{ t('day') }}
</div>
<p class="text-[12px] text-[#999]">{{ t('advanceTips') }}</p>
</div>
</el-form-item>
<el-form-item :label="t('mostDays')" prop="most_day">
<div>
<div class="flex">
{{ t('reservationAvailable') }}
<div class="w-[100px] mx-[5px]">
<el-input v-model.trim="formData.most_day" />
</div>
{{ t('withinDays') }}
</div>
<p class="text-[12px] text-[#999]">{{ t('mostDaysTips') }}</p>
</div>
</el-form-item>
</template>
<el-form-item :label="t('deliveryAddress')" prop="delivery_address">
<div class="flex flex-col">
<div class="flex">
{{ defaultDeliveryAddress ? defaultDeliveryAddress.full_address :
t('defaultDeliveryAddressEmpty') }}
<el-button type="primary" @click="router.push('/shop/order/address')" link
class="ml-[10px]">{{ defaultDeliveryAddress ? t('update') : t('toSetting')
}}</el-button>
</div>
<div class="text-error leading-none"
v-if="formData.center.lat && defaultDeliveryAddress && (formData.center.lat != defaultDeliveryAddress.lat || formData.center.lng != defaultDeliveryAddress.lng)">
{{ t('deliveryAddressChange') }}</div>
</div>
</el-form-item>
<el-form-item :label="t('feeType')">
<el-radio-group v-model="formData.fee_type">
<el-radio label="region">{{ t('region') }}</el-radio>
<el-radio label="distance">{{ t('distance') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('feeSetting')" prop="distance" v-show="formData.fee_type == 'distance'">
<div class="flex">
<div class="w-[60px] mx-[5px]">
<el-input v-model.number="formData.base_dist" type="text" maxlength="6" @keyup="filterDigit($event)" />
</div>
{{ t('feeSettingTextOne') }}
<div class="w-[60px] mx-[5px]">
<el-input v-model.trim="formData.base_price" type="text" maxlength="8" @keyup="filterDigit($event)" />
</div>
{{ t('feeSettingTextTwo') }}
<div class="w-[60px] mx-[5px]">
<el-input v-model.number="formData.grad_dist" type="text" maxlength="6" @keyup="filterDigit($event)" />
</div>
{{ t('feeSettingTextThree') }}
<div class="w-[60px] mx-[5px]">
<el-input v-model.trim="formData.grad_price" type="text" maxlength="8" @keyup="filterDigit($event)" />
</div>
{{ t('priceUnit') }}
</div>
</el-form-item>
<el-form-item :label="t('weightFee')" prop="">
<div class="flex">
{{ t('weightFeeTextOne') }}
<div class="w-[60px] mx-[5px]">
<el-input v-model.trim="formData.weight_start" type="text" maxlength="6" @keyup="filterDigit($event)" />
</div>
{{ t('weightFeeTextTwo') }}
<div class="w-[60px] mx-[5px]">
<el-input v-model.trim="formData.weight_unit" type="text" maxlength="6" @keyup="filterDigit($event)" />
</div>
{{ t('weightFeeTextThree') }}
<div class="w-[60px] mx-[5px]">
<el-input v-model.trim="formData.weight_price" type="text" maxlength="8" @keyup="filterDigit($event)" />
</div>
{{ t('priceUnit') }}
</div>
</el-form-item>
<el-form-item prop="area" v-loading="mapLoading">
<div class="relative w-full">
<div id="container" class="w-full h-[520px]"></div>
<div class="absolute bg-white w-[270px] h-[500px] top-[10px] left-[10px] region-list">
<el-scrollbar>
<div class="p-[10px] region-item pr-[50px] relative"
v-for="(item, index) in formData.area" :key="index"
:class="{ '!border-primary': index == currArea }" @click="selectArea(index)">
<el-form label-width="80px" :model="item" :rules="formRules" class="page-form"
ref="areaFromRef">
<div class="pb-[18px]">
<el-form-item :label="t('areaName')" prop="area_name">
<el-input v-model.trim="formData.area[index].area_name" type="text" />
</el-form-item>
</div>
<div class="pb-[18px]">
<el-form-item :label="t('startPrice')" prop="start_price">
<el-input v-model.trim="formData.area[index].start_price" maxlength="8" type="text"
@keyup="filterDigit($event)" />
</el-form-item>
</div>
<div class="pb-[10px]" v-show="formData.fee_type == 'region'">
<el-form-item :label="t('deliveryPrice')" prop="delivery_price">
<el-input v-model.trim="formData.area[index].delivery_price" type="text"
@keyup="filterDigit($event)" />
</el-form-item>
</div>
<el-form-item :label="t('areaType')">
<el-radio-group v-model="formData.area[index].area_type" @change="areaTypeChange(index)">
<el-radio label="radius" size="large" class="!mr-[10px]">{{ t('radius') }}</el-radio>
<el-radio label="custom" size="large" class="!mr-[0px]">{{ t('custom') }}</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<el-button type="primary" link class="absolute z-1 top-[10px] right-[10px]"
@click.stop="deleteArea(index)">{{ t('delete') }}</el-button>
</div>
<div class="p-[10px] text-center">
<el-button plain @click="addArea">{{ t('addDeliveryArea') }}</el-button>
</div>
</el-scrollbar>
</div>
</div>
</el-form-item>
</el-form>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="onSave(formRef)" :disabled="loading">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted, onBeforeUnmount,toRaw } from 'vue'
import { t } from '@/lang'
import { ArrowLeft } from "@element-plus/icons-vue"
import { useRoute, useRouter } from 'vue-router'
import { getMap } from '@/app/api/sys'
import { guid, filterDigit, deepClone } from '@/utils/common'
import { createCircle, deleteGeometry, createPolygon, selectGeometry, createMarker } from '@/utils/qqmap'
import { setLocal, getLocal ,getThird} from '@/addon/shop/api/delivery'
import { FormInstance } from 'element-plus'
import Test from '@/utils/test'
import { getShopDefaultDeliveryAddressInfo } from '@/addon/shop/api/shop_address'
const route = useRoute()
const router = useRouter()
const loading = ref(true)
const pageName = route.meta.title
const formRef = ref<FormInstance>()
const areaFromRef: any = ref<FormInstance[]>()
interface addressType{
full_address:string
lat:string
lng:string
}
const defaultDeliveryAddress:any = ref<addressType|null>(null)
const getDefaultDeliveryAddress = async () => {
await getShopDefaultDeliveryAddressInfo().then(({ data }) => {
defaultDeliveryAddress.value = data
}).catch()
}
getDefaultDeliveryAddress()
const thirdPartyData = ref<any>(null)
const getThirdFn = ()=> {
getThird().then((res) => {
thirdPartyData.value = res.data.map((item: any) => ({
...item,
isEnabled: false, //
config: {
app_key: '',
app_secret: '',
shop_id: '',
shop_store_id: ''
}
}));
})
}
const activeService = computed(() => {
if (!thirdPartyData.value || thirdPartyData.value.length === 0) {
return null;
}
return thirdPartyData.value.find(service => service.isEnabled) || null;
});
//
const handleServiceChange = (index: number) => {
const service = thirdPartyData.value[index];
if (service.isEnabled) {
thirdPartyData.value.forEach((s, i) => {
if (i !== index) {
s.isEnabled = false;
}
});
}
};
const buildThirdPartyConfig = () => {
const config: Record<string, any> = {};
thirdPartyData.value.forEach(service => {
config[service.type] = {
app_key: service.config.app_key,
app_secret: service.config.app_secret,
shop_id: service.config.shop_id,
shop_store_no: service.config.shop_store_no
};
if (service.isEnabled) {
config.default = service.type;
}
});
return config;
};
getThirdFn()
const formData = ref({
center: {
lat: '',
lng: ''
},
// delivery_type: ['business'],
delivery_type: 'business',
fee_type: 'region',
time_is_open:1,
time_type:0,
time_week: <any>[],
base_dist: '',
base_price: '',
grad_dist: '',
grad_price: '',
weight_start: 0.000,
weight_unit: 0,
weight_price: 0,
area: [
{
area_name: '',
area_type: 'radius',
start_price: 0,
delivery_price: 0,
area_json: {
key: guid()
}
}
],
delivery_time: [
{ start_time: '', end_time: '' } //
],
time_interval: 30,
most_day: 7,
advance_day: 0,
time_most: 1,
time_advance:1,
third_party_config:{}
})
//
const regExp = {
required: /[\S]+/,
number: /^\d{0,10}$/,
digit: /^\d{0,10}(.?\d{0,2})$/,
special: /^\d{0,10}(.?\d{0,3})$/
}
//
const formRules = computed(() => {
return {
time_week: [{ required: true, message: t('timeWeekRequire'), trigger: 'change' }],
delivery_address: [
{
validator: (rule: any, value: any, callback: any) => {
if (!defaultDeliveryAddress.value) {
callback(new Error(t('defaultDeliveryAddressEmpty')))
}
callback()
}
}
],
// delivery_type: [
// {
// validator: (rule: any, value: any, callback: any) => {
// if (!formData.value.delivery_type.length) {
// callback(new Error(t('deliveryTypeRequire')))
// }
// callback()
// }
// }
// ],
distance: [
{
validator: (rule: any, value: any, callback: any) => {
if (formData.value.fee_type == 'distance') {
if (Test.require(formData.value.base_dist)) {
callback(new Error(t('baseDistRequire')))
}
if (Test.require(formData.value.base_price)) {
callback(new Error(t('basePriceRequire')))
}
if (Test.require(formData.value.grad_dist)) {
callback(new Error(t('gradDistRequire')))
}
if (Test.require(formData.value.grad_price)) {
callback(new Error(t('gradPriceRequire')))
}
}
callback()
},
trigger: 'blur'
}
],
area_name: [{ required: true, message: t('areaNameRequire'), trigger: 'blur' }],
start_price: [
{ required: true, message: t('startPriceRequire'), trigger: 'blur' },
{
validator: (rule: any, value: any, callback: any) => {
if (parseInt(value) < 0) {
callback(new Error(t('startPriceMin')))
}
callback()
},
trigger: 'blur'
}
],
delivery_price: [
{ required: formData.value.fee_type == 'region', message: t('deliveryPriceRequire'), trigger: 'blur' },
{
validator: (rule: any, value: any, callback: any) => {
if (parseInt(value) < 0) {
callback(new Error(t('deliveryPriceMin')))
}
callback()
},
trigger: 'blur'
}
],
area: [
{
validator: (rule: any, value: any, callback: any) => {
if (Test.empty(formData.value.area)) {
callback(new Error(t('areaPlaceholder')))
}
callback()
},
trigger: 'blur'
}
],
delivery_time: [
{
validator: (rule: any, value: any, callback: any) => {
if (!value || value.length === 0) {
return callback(new Error(t('tradeTimePlaceholderTwo')))
}
for (let i = 0; i < value.length; i++) {
const timeRange = value[i]
if (!timeRange.start_time || !timeRange.end_time) {
return callback(new Error(t('tradeTimePlaceholderTwo')))
}
//
if (timeRange.end_time <= timeRange.start_time) {
return callback(new Error(t('tradeTimePlaceholderFour')))
}
//
if (i > 0 && value[i].start_time < value[i - 1].end_time) {
return callback(new Error(t('tradeTimePlaceholderFive')))
}
}
callback()
},
trigger: 'change',
required: true
}
],
time_interval: [
{ required: true, message: t('tradeTimePlaceholderThree'), trigger: 'change' }
],
advance_day:[
{
validator(rule, value, callback) {
if (value === null || value === '') {
callback()
} else if (isNaN(value) || !regExp.number.test(value)) {
callback(t('formatError'))
} else if (value < 0) {
callback(t('notLessThanZero'))
} else {
callback();
}
},
trigger: 'blur'
}
],
most_day:[
{
validator(rule, value, callback) {
if (value === null || value === '') {
callback()
} else if (isNaN(value) || !regExp.number.test(value)) {
callback(t('formatError'))
} else if (value <= 0) {
callback(t('mustBeGreaterThanZero'))
} else {
callback();
}
},
trigger: 'blur'
}
]
}
})
const validateThirdPartyConfig = () => {
if (formData.value.delivery_type !== 'third') return true;
const active = thirdPartyData.value.find(item => item.isEnabled);
if (!active) {
ElMessage.error(t('thridRequire'));
return false;
}
const { app_key, app_secret, shop_id ,shop_store_no} = active.config;
if (!app_key || !app_secret || !shop_id || !shop_store_no) {
ElMessage.error(t('thridSeting'));
return false;
}
return true;
};
//
const addTimeRange = () => {
formData.value.delivery_time.push({ start_time: '', end_time: '' })
}
//
const removeTimeRange = (index: number) => {
formData.value.delivery_time.splice(index, 1)
}
const timeTransition = (time:any) => {
const arr = time.split(':')
const num = arr[0] * 60 * 60 + arr[1] * 60
return num
}
const timestampTransition = (timeStamp:any) => {
let hour = Math.floor(timeStamp / (60 * 60))
let minute = Math.floor(timeStamp / 60) - (hour * 60)
hour = hour < 10 ? ('0' + hour) : hour
minute = minute < 10 ? ('0' + minute) : minute
return hour + ':' + minute
}
getLocal().then(({ data }) => {
loading.value = false
if (data) Object.assign(formData.value, data)
formData.value.time_week = formData.value.time_week?formData.value.time_week.split(','):[]
if (Array.isArray(data.delivery_type) && data.delivery_type.length > 0) {
formData.value.delivery_type = data.delivery_type[0];
} else {
formData.value.delivery_type = data.delivery_type || 'business';
}
// delivery_time
if (data.delivery_time === '' || data.delivery_time === null) {
formData.value.delivery_time = [{ start_time: '', end_time: '' }];
} else if (Array.isArray(data.delivery_time)) {
formData.value.delivery_time = data.delivery_time.map((item: any) => ({
start_time: timestampTransition(item.start_time),
end_time: timestampTransition(item.end_time)
}));
}
// third_party_config thirdPartyData
if (data.third_party_config) {
thirdPartyData.value.forEach(service => {
const config = data.third_party_config[service.type];
if (config) {
service.isEnabled = service.type == data.third_party_config.default;
service.config = {
app_key: config.app_key || '',
app_secret: config.app_secret || '',
shop_id: config.shop_id || '',
shop_store_id: config.shop_store_id || '',
shop_store_no: config.shop_store_no || '',
};
}
});
}
}).catch(()=>{
loading.value = false
})
onMounted(() => {
const mapScript = document.createElement('script')
getMap().then(res => {
mapScript.type = 'text/javascript'
mapScript.src = 'https://map.qq.com/api/gljs?libraries=tools,service&v=1.exp&key=' + res.data.key
document.body.appendChild(mapScript)
})
mapScript.onload = () => {
setTimeout(() => {
initMap()
}, 500)
}
})
/**
* 初始化地图
*/
let map: any
const mapLoading = ref(true)
const initMap = () => {
const TMap = (window as any).TMap
const LatLng = TMap.LatLng
const center = new LatLng(defaultDeliveryAddress.value ? defaultDeliveryAddress.value.lat : 39.980619, defaultDeliveryAddress.value ? defaultDeliveryAddress.value.lng : 116.321277)
map = new TMap.Map('container', {
center,
zoom: 14
})
createMarker(map)
map.on('tilesloaded', () => {
mapLoading.value = false
})
formData.value.area.forEach(item => {
item.area_type == 'radius' ? createCircle(map, item.area_json) : createPolygon(map, item.area_json)
})
}
const currArea = ref<number>(0)
/**
* 添加配送区域
*/
const addArea = () => {
formData.value.area.push({
area_name: '',
area_type: 'radius',
start_price: 0,
delivery_price: 0,
area_json: {
key: guid()
}
})
const index = formData.value.area.length - 1
createCircle(map, formData.value.area[index].area_json)
}
/**
* 删除配送区域
*/
const deleteArea = (index: number) => {
const data = formData.value.area[index]
deleteGeometry(data.area_json.key)
formData.value.area.splice(index, 1)
}
const selectArea = (index: number) => {
currArea.value = index
const data = formData.value.area[index]
selectGeometry(data.area_json.key)
}
const areaTypeChange = (index: number) => {
const data = formData.value.area[index]
deleteGeometry(data.area_json.key)
data.area_type == 'radius' ? createCircle(map, data.area_json) : createPolygon(map, data.area_json)
}
onBeforeUnmount(() => {
map.destroy()
})
const onSave = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async (valid) => {
let areaValidate = true
for (let i = 0; i < areaFromRef.value?.length; i++) {
const ref = areaFromRef.value[i]
await ref.validate(async (valid) => {
areaValidate = valid
})
if (!areaValidate) break
}
if (!areaValidate) return
if (!validateThirdPartyConfig()) return;
if (valid) {
loading.value = true
formData.value.center = {
lat: defaultDeliveryAddress.value.lat,
lng: defaultDeliveryAddress.value.lng
}
await formEl.validate(async (valid) => {
const param = deepClone(toRaw(formData.value))
param.time_week = param.time_week.toString()
param.delivery_time = param.delivery_time.map(range => ({
start_time: range.start_time ? timeTransition(range.start_time) : null,
end_time: range.end_time ? timeTransition(range.end_time) : null
}))
param.third_party_config = buildThirdPartyConfig()
param.delivery_type = [param.delivery_type]
setLocal(param).then(() => {
loading.value = false
}).catch(() => {
loading.value = false
})
})
}
})
}
const back = () => {
router.push({ path: '/shop/order/delivery' })
}
</script>
<style lang="scss" scoped>
.region-list {
border: 1px solid var(--el-border-color-lighter);
z-index: 3;
.region-item {
border: 1px solid transparent;
border-bottom-color: var(--el-border-color-lighter);
}
}
#container :deep(div){
z-index: 2 !important;
}
</style>

View File

@ -0,0 +1,164 @@
<template>
<div class="main-container">
<el-card class="card !border-none mb-[15px]" shadow="never">
<el-page-header :content="pageName" :icon="ArrowLeft" @back="router.push({ path: '/shop/order/delivery' })" />
</el-card>
<el-form :model="formData" label-width="150px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
<el-card class="box-card !border-none" shadow="never">
<el-form-item :label="t('interfaceType')" prop="interface_type">
<div>
<el-radio-group v-model="formData.interface_type">
<el-radio :label="1" size="large">{{ t('kdn') }}</el-radio>
<el-radio :label="2" size="large">{{ t('kd100') }}</el-radio>
</el-radio-group>
<p class="text-[12px] text-[#b2b2b2]" v-if="formData.interface_type == 1">
{{ t('promptTips1-1') }}
<el-button class="button-size" type="primary" link @click="openEvent('https://www.kdniao.com/reg?from=niucloud')">https://www.kdniao.com</el-button>
</p>
<p class="text-[12px] text-[#b2b2b2]" v-if="formData.interface_type == 2">
{{ t('promptTips2') }}<el-button class="button-size" type="primary" link @click="openEvent('https://www.kuaidi100.com')">https://www.kuaidi100.com</el-button>
</p>
</div>
</el-form-item>
<div v-if="formData.interface_type == 1">
<el-form-item :label="t('isPayEdition')" prop="kdn_is_pay">
<div>
<el-radio-group v-model="formData.kdniao_is_pay">
<el-radio :label="1" size="large">{{ t('PayPerOrder') }}</el-radio>
<el-radio :label="2" size="large">{{ t('PayPerUse') }}</el-radio>
</el-radio-group>
<p class="text-[12px] text-[#b2b2b2]" v-if="formData.interface_type == 1">
{{ t('promptTips1-2') }}
</p>
<p class="text-[12px] text-[#b2b2b2]" v-if="formData.interface_type == 1">
{{ t('promptTips1-3') }}
</p>
</div>
</el-form-item>
<el-form-item label="EBusinessID" class="input-item">
<div>
<el-input v-model.trim="formData.kdniao_id" :placeholder="t('kdnEBusinessIDPlaceholder')" class="input-width" clearable />
<p class="text-[12px] text-[#b2b2b2]">{{ t('kdnEBusinessIDTips') }}</p>
</div>
</el-form-item>
<el-form-item label="APPKEY" class="input-item">
<div>
<el-input v-model.trim="formData.kdniao_app_key" clearable :placeholder="t('kdnAppKeyPlaceholder')" class="input-width" />
<p class="text-[12px] text-[#b2b2b2]">{{ t('kdnAppKeyTips') }}</p>
</div>
</el-form-item>
</div>
<div v-if="formData.interface_type == 2">
<el-form-item label="APPKEY" class="input-item">
<div>
<el-input v-model.trim="formData.kd100_app_key" clearable :placeholder="t('kd100AppKeyPlaceholder')" class="input-width" />
<p class="text-[12px] text-[#b2b2b2]">{{ t('kd100AppKeyTips') }}</p>
</div>
</el-form-item>
<el-form-item label="CUSTOMER" class="input-item">
<div>
<el-input v-model.trim="formData.kd100_customer" :placeholder="t('kd100CustomerPlaceholder')" class="input-width" clearable />
<p class="text-[12px] text-[#b2b2b2]">{{ t('kd100CustomerTips') }}</p>
</div>
</el-form-item>
</div>
</el-card>
</el-form>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" :loading="loading" @click="save(formRef)">{{ t('save') }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import { ArrowLeft } from '@element-plus/icons-vue'
import { FormInstance, FormRules } from 'element-plus'
import { useRoute, useRouter } from 'vue-router'
import { setDeliverySearch, getDeliverySearch } from '@/addon/shop/api/delivery'
const route = useRoute()
const router = useRouter()
const pageName = route.meta.title
const loading = ref(true)
interface formDataType {
interface_type: number
kdniao_id: string
kdniao_app_key: string
kdniao_is_pay: number
kd100_app_key: string
kd100_customer: string
}
const formData = reactive<formDataType | any>({
interface_type: 1,
kdniao_id: '',
kdniao_app_key: '',
kdniao_is_pay: 1,
kd100_app_key: '',
kd100_customer: ''
})
const setFormData = async() => {
const data = await (await getDeliverySearch()).data
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
loading.value = false
}
setFormData()
const openEvent = (url: any) => {
window.open(url, '_blank')
}
const formRef = ref<FormInstance>()
//
const formRules = reactive<FormRules>({})
/**
* 保存
*/
const save = async(formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async(valid) => {
if (valid) {
loading.value = true
setDeliverySearch(formData).then(() => {
loading.value = false
}).catch(() => {
loading.value = false
})
}
})
}
</script>
<style lang="scss" scoped>
.input-item {
margin-bottom: 10px !important
}
.button-size {
font-size: 12px !important;
}
.el-radio.el-radio--large {
height: auto !important
}
</style>

View File

@ -0,0 +1,139 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<el-page-header :content="pageName" :icon="ArrowLeft" @back="$router.back()" />
</el-card>
<el-card class="box-card mt-[15px] !border-none" shadow="never">
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<div class="flex justify-between items-center">
<el-form :inline="true" :model="tableData.searchParam" ref="searchFormRef">
<el-form-item :label="t('deliverName')" prop="deliver_name">
<el-input v-model.trim="tableData.searchParam.deliver_name" :placeholder="t('deliverNamePlaceholder')" />
</el-form-item>
<el-form-item :label="t('deliverMobile')" prop="deliver_mobile">
<el-input v-model.trim="tableData.searchParam.deliver_mobile" :placeholder="t('deliverMobilePlaceholder')" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="getShopDeliveryFn()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
<el-button type="primary" @click="addEvent">
{{ t('addDeliveryPersonnel') }}
</el-button>
</div>
</el-card>
<div class="mt-[10px]">
<el-table :data="tableData.data" ref="tableRef" size="large" v-loading="tableData.loading">
<template #empty>
<span>{{ !tableData.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column prop="deliver_name" :label="t('deliverName')" />
<el-table-column prop="deliver_mobile" :label="t('deliverMobile')" />
<el-table-column :label="t('operation')" fixed="right" align="right" width="120">
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.deliver_id)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.limit"
layout="total, sizes, prev, pager, next, jumper" :total="tableData.total"
@size-change="getShopDeliveryFn()" @current-change="getShopDeliveryFn" />
</div>
</div>
</el-card>
<delivery-personnel-edit ref="editCategoryDialog" @complete="getShopDeliveryFn(getTablePageStorage(tableData.searchParam).page)" />
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import deliveryPersonnelEdit from '@/addon/shop/views/delivery/components/delivery-personnel-edit.vue'
import { getShopDelivery, deleteShopDeliver } from '@/addon/shop/api/delivery'
import { useRoute, useRouter } from 'vue-router'
import { ElMessageBox, FormInstance } from 'element-plus'
import { ArrowLeft } from '@element-plus/icons-vue'
import { setTablePageStorage, getTablePageStorage } from '@/utils/common'
const route = useRoute()
const router = useRouter()
const pageName = route.meta.title
const tableData = reactive({
page: 1,
limit: 10,
total: 0,
loading: false,
data: [],
searchParam: {
deliver_name: '',
deliver_mobile: ''
}
})
const searchFormRef = ref<FormInstance>()
/**
* 获取配送员列表
*/
const getShopDeliveryFn = (page: number = 1) => {
tableData.loading = true
tableData.page = page
getShopDelivery({
page: tableData.page,
limit: tableData.limit,
...tableData.searchParam
}).then(res => {
tableData.loading = false
tableData.data = res.data.data
tableData.total = res.data.total
setTablePageStorage(tableData.page, tableData.limit, tableData.searchParam)
}).catch(() => {
tableData.loading = false
})
}
getShopDeliveryFn(getTablePageStorage(tableData.searchParam).page)
const editCategoryDialog: Record<string, any> | null = ref(null)
/**
* 添加配送员
*/
const addEvent = () => {
editCategoryDialog.value.setFormData()
editCategoryDialog.value.showDialog = true
}
/**
* 编辑配送员
* @param data
*/
const editEvent = (data: any) => {
editCategoryDialog.value.setFormData(data)
editCategoryDialog.value.showDialog = true
}
/**
* 删除配送员
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('deliverDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}
).then(() => {
deleteShopDeliver(id).then(() => {
getShopDeliveryFn(getTablePageStorage(tableData.searchParam).page)
}).catch(() => {
})
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
getShopDeliveryFn()
}
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,164 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<el-page-header :content="pageName" :icon="ArrowLeft" @back="router.push('/shop/order/delivery')" />
</el-card>
<el-card class="box-card mt-[15px] !border-none" shadow="never">
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<div class="flex justify-between items-center">
<el-form :inline="true" :model="storeTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('storeName')" prop="store_name">
<el-input v-model.trim="storeTable.searchParam.store_name" :placeholder="t('storeNamePlaceholder')" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadStoreList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
<el-button type="primary" @click="addEvent">
{{ t('addStore') }}
</el-button>
</div>
</el-card>
<div class="mt-[10px]">
<el-table :data="storeTable.data" size="large" v-loading="storeTable.loading">
<template #empty>
<span>{{ !storeTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column :label="t('storeInfo')" min-width="170" align="left">
<template #default="{ row }">
<div class="h-[50px] flex items-center">
<el-image class="w-[50px] h-[50px] " :src="img(row.store_logo)" fit="contain">
<template #error>
<div class="image-slot">
<img class="w-[50px] h-[50px]" src="@/addon/shop/assets/store_default.png" />
</div>
</template>
</el-image>
<p class="ml-[10px] text-[14px]">{{ row.store_name }}</p>
</div>
</template>
</el-table-column>
<el-table-column prop="store_mobile" :label="t('storeMobile')" min-width="120" />
<el-table-column prop="full_address" :label="t('fullAddress')" min-width="180" />
<el-table-column prop="trade_time" :label="t('tradeTime')" min-width="120" />
<el-table-column :label="t('createTime')" min-width="120">
<template #default="{ row }">
{{ row.create_time || '' }}
</template>
</el-table-column>
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="120">
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.store_id)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination v-model:current-page="storeTable.page" v-model:page-size="storeTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="storeTable.total"
@size-change="loadStoreList()" @current-change="loadStoreList" />
</div>
</div>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import { getStoreList, deleteStore } from '@/addon/shop/api/delivery'
import { img } from '@/utils/common'
import { ElMessageBox, FormInstance } from 'element-plus'
import { ArrowLeft } from '@element-plus/icons-vue'
import { useRouter, useRoute } from 'vue-router'
import { setTablePageStorage,getTablePageStorage } from "@/utils/common";
const route = useRoute()
const pageName = route.meta.title
const storeTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {
store_name: '',
create_time: ''
}
})
const searchFormRef = ref<FormInstance>()
/**
* 获取自提门店列表
*/
const loadStoreList = (page: number = 1) => {
storeTable.loading = true
storeTable.page = page
getStoreList({
page: storeTable.page,
limit: storeTable.limit,
...storeTable.searchParam
}).then(res => {
storeTable.loading = false
storeTable.data = res.data.data
storeTable.total = res.data.total
setTablePageStorage(storeTable.page, storeTable.limit, storeTable.searchParam);
}).catch(() => {
storeTable.loading = false
})
}
loadStoreList(getTablePageStorage(storeTable.searchParam).page);
const router = useRouter()
/**
* 添加自提门店
*/
const addEvent = () => {
router.push('/shop/order/delivery/store/edit')
}
/**
* 编辑自提门店
* @param data
*/
const editEvent = (data: any) => {
router.push('/shop/order/delivery/store/edit?id=' + data.store_id)
}
/**
* 删除自提门店
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('storeDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}
).then(() => {
deleteStore(id).then(() => {
loadStoreList(getTablePageStorage(storeTable.searchParam).page);
}).catch(() => {
})
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadStoreList()
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,531 @@
<template>
<div class="main-container">
<el-card class="card !border-none" shadow="never">
<el-page-header :content="id ? t('updateStore') : t('addStore')" :icon="ArrowLeft" @back="back" />
</el-card>
<el-card class="box-card mt-[15px] !border-none" shadow="never" v-loading="loading">
<el-form :model="formData" label-width="140px" ref="formRef" :rules="formRules" class="page-form">
<el-form-item :label="t('storeName')" prop="store_name">
<el-input v-model.trim="formData.store_name" clearable :placeholder="t('storeNamePlaceholder')" class="input-width" maxlength="30" />
</el-form-item>
<el-form-item :label="t('storeDesc')">
<el-input v-model.trim="formData.store_desc" type="textarea" rows="4" clearable :placeholder="t('storeDescPlaceholder')" class="input-width" maxlength="200" />
</el-form-item>
<el-form-item :label="t('storeLogo')">
<upload-image v-model="formData.store_logo" />
</el-form-item>
<el-form-item :label="t('storeMobile')" prop="store_mobile">
<el-input v-model.trim="formData.store_mobile" clearable :placeholder="t('storeMobilePlaceholder')" class="input-width" @keyup="filterNumber($event)" @blur="formData.store_mobile = $event.target.value" maxlength="11" />
</el-form-item>
<el-form-item :label="t('tradeTime')" prop="trade_time">
<div>
<el-input v-model.trim="formData.trade_time" clearable :placeholder="t('tradeTimePlaceholder')" class="input-width" />
<p class="text-[12px] text-[#999]">{{ t('tradeTimeTips') }}</p>
</div>
</el-form-item>
<el-form-item :label="t('storeDate')" prop="time_week">
<el-checkbox-group v-model="formData.time_week">
<el-checkbox :label="'1'">{{ t('monday') }}</el-checkbox>
<el-checkbox :label="'2'">{{ t('tuesday') }}</el-checkbox>
<el-checkbox :label="'3'">{{ t('wednesday') }}</el-checkbox>
<el-checkbox :label="'4'">{{ t('thursday') }}</el-checkbox>
<el-checkbox :label="'5'">{{ t('friday') }}</el-checkbox>
<el-checkbox :label="'6'">{{ t('saturday') }}</el-checkbox>
<el-checkbox :label="'0'">{{ t('sunday') }}</el-checkbox>
<br />
</el-checkbox-group>
</el-form-item>
<el-form-item :label="t('storeTime')" prop="trade_time_json">
<div>
<div>
<div v-for="(timeRange, index) in formData.trade_time_json" :key="index" class="mb-3">
<el-time-picker v-model="timeRange.start_time" :placeholder="t('startTime')" format="HH:mm" value-format="HH:mm" :picker-options="{selectableRange: '00:00 - 23:59'}"/>
<span class="mx-2">-</span>
<el-time-picker v-model="timeRange.end_time" :placeholder="t('endTime')" format="HH:mm" value-format="HH:mm" :picker-options="{selectableRange: '00:00 - 23:59'}"/>
<span v-if="index > 0" class="text-primary cursor-pointer ml-[10px]" @click="removeTimeRange(index)"> {{ t('delete') }}</span>
</div>
<span class="text-primary cursor-pointer mr-[10px]" @click="addTimeRange" v-if="formData.trade_time_json.length < 3"> {{ t('addTimeRange') }}</span>
</div>
<div class="text-[12px] text-[#999]">{{ t('storeDateTips') }}</div>
</div>
</el-form-item>
<el-form-item :label="t('storeTimeInterval')" prop="time_interval">
<div>
<el-radio-group v-model="formData.time_interval">
<el-radio v-for="(item, key) in time_interval_list" :key="key" :label="item.type">{{ item.name }}</el-radio>
</el-radio-group>
<p class="text-[12px] text-[#999]">{{ t('storeTimeIntervalTips') }}</p>
</div>
</el-form-item>
<el-form-item :label="t('storeAddress')" prop="address_area">
<el-select v-model="formData.province_id" value-key="id" clearable class="w-[200px]" ref="provinceRef">
<el-option :label="t('provincePlaceholder')" :value="0"/>
<el-option v-for="(item, index) in areaList.province" :key="index" :label="item.name" :value="item.id"/>
</el-select>
<el-select v-model="formData.city_id" value-key="id" clearable class="w-[200px] ml-3" ref="cityRef">
<el-option :label="t('cityPlaceholder')" :value="0"/>
<el-option v-for="(item, index) in areaList.city " :key="index" :label="item.name" :value="item.id"/>
</el-select>
<el-select v-model="formData.district_id" value-key="id" clearable class="w-[200px] ml-3" ref="districtRef">
<el-option :label="t('districtPlaceholder')" :value="0"/>
<el-option v-for="(item, index) in areaList.district " :key="index" :label="item.name" :value="item.id"/>
</el-select>
</el-form-item>
<el-form-item prop="address">
<el-input v-model.trim="formData.address" clearable :placeholder="t('addressPlaceholder')" @input="areaChange()" class="input-width"/>
</el-form-item>
<el-form-item>
<div id="container" class="w-[800px] h-[520px] relative" v-loading="mapLoading"></div>
</el-form-item>
</el-form>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer !z-[1000]">
<el-button type="primary" @click="onSave(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, onMounted, watch } from 'vue'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import { ArrowLeft } from '@element-plus/icons-vue'
import { getStoreInfo, addStore, editStore, getStoreInit } from '@/addon/shop/api/delivery'
import { getMap, getAreaListByPid, getAreaByCode } from '@/app/api/sys'
import { useRoute } from 'vue-router'
import { createMarker, latLngToAddress, addressToLatLng } from '@/utils/qqmap'
import { filterNumber, debounce } from '@/utils/common'
import { cloneDeep } from 'lodash-es'
const route = useRoute()
const id: number = parseInt(route.query.id as string)
const loading = ref(false)
interface areaType{
province: any[],
city: any[],
district: any[]
}
const areaList = reactive<areaType>({
province: [],
city: [],
district: []
})
const provinceRef = ref()
const cityRef = ref()
const districtRef = ref()
/**
* 获取省
*/
getAreaListByPid(0).then(res => {
areaList.province = res.data
})
let mapKey: string = ''
onMounted(() => {
const mapScript = document.createElement('script')
getMap().then(res => {
mapKey = res.data.key
mapScript.type = 'text/javascript'
mapScript.src = 'https://map.qq.com/api/gljs?libraries=tools,service&v=1.exp&key=' + res.data.key
document.body.appendChild(mapScript)
})
mapScript.onload = () => {
setTimeout(() => {
initMap()
}, 500)
}
getStoreInitFn()
})
const week_list = ref({})
const time_interval_list = ref({})
const getStoreInitFn = () => {
getStoreInit().then(res => {
week_list.value = res.data.week_list
time_interval_list.value = res.data.time_interval_list
})
}
/**
* 初始化地图
*/
let map: any
let marker: any
const mapLoading = ref(true)
const initMap = () => {
const TMap = (window as any).TMap
const LatLng = TMap.LatLng
const center = new LatLng(formData.latitude, formData.longitude)
map = new TMap.Map('container', {
center,
zoom: 14
})
map.on('tilesloaded', () => {
mapLoading.value = false
})
marker = createMarker(map)
map.on('click', (evt: any) => {
map.setCenter(evt.latLng)
marker.updateGeometries({
id: 'center',
position: evt.latLng
})
latLngChange(evt.latLng.lat, evt.latLng.lng)
})
latLngChange(center.lat, center.lng)
}
const storeArea = reactive({
province_id: 0,
city_id: 0,
district_id: 0
})
const latLngChange = (lat: number, lng: number) => {
latLngToAddress({ mapKey, lat, lng }).then(({ message, result }) => {
if (message == 'query ok' || message == 'Success') {
formData.latitude = result.location.lat
formData.longitude = result.location.lng
formData.address = result.formatted_addresses.recommend
getAreaByCode(result.ad_info.adcode).then(({ data }) => {
storeArea.province_id = data.province ? data.province.id : 0
storeArea.city_id = data.city ? data.city.id : 0
storeArea.district_id = data.district ? data.district.id : 0
})
} else {
console.error(message, result)
}
}).catch(err => {
console.log(err)
})
}
/**
* 表单数据
*/
const initialFormData = {
store_id: 0,
store_name: '',
store_desc: '',
store_logo: '',
store_mobile: '',
province_id: 0,
province_name: '',
city_id: 0,
city_name: '',
district_id: 0,
district_name: '',
address: '',
full_address: '',
longitude: 116.397190,
latitude: 39.908626,
trade_time: '',
time_week: ['1', '2', '3', '4', '5', '6', '0'],
trade_time_json: [
{ start_time: '', end_time: '' } //
],
time_interval: 30
}
const formData: Record<string, any> = reactive({ ...initialFormData })
//
const addTimeRange = () => {
formData.trade_time_json.push({ start_time: '', end_time: '' })
}
//
const removeTimeRange = (index: number) => {
formData.trade_time_json.splice(index, 1)
}
const timeTransition = (time:any) => {
const arr = time.split(':')
const num = arr[0] * 60 * 60 + arr[1] * 60
return num
}
const timestampTransition = (timeStamp:any) => {
let hour = Math.floor(timeStamp / (60 * 60))
let minute = Math.floor(timeStamp / 60) - (hour * 60)
hour = hour < 10 ? ('0' + hour) : hour
minute = minute < 10 ? ('0' + minute) : minute
return hour + ':' + minute
}
const setFormData = async (id: number = 0) => {
loading.value = true
Object.assign(formData, initialFormData)
const data = await (await getStoreInfo(id)).data
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
if (key == "trade_time_json" && Array.isArray(data[key])) {
formData[key] = data[key].map((item: any) => ({
start_time: timestampTransition(item.start_time),
end_time: timestampTransition(item.end_time)
}))
}
})
loading.value = false
}
if (id) setFormData(id)
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
store_name: [
{ required: true, message: t('storeNamePlaceholder'), trigger: 'blur' }
],
store_logo: [
{ required: true, message: t('storeLogoPlaceholder'), trigger: 'blur' }
],
store_mobile: [
{ required: true, message: t('storeMobilePlaceholder'), trigger: 'blur' }
],
trade_time: [
{ required: true, message: t('tradeTimePlaceholder'), trigger: 'blur' }
],
address_area: [
{
validator: (rule: any, value: any, callback: any) => {
if (!formData.province_id) {
callback(new Error(t('provincePlaceholder')))
}
if (!formData.city_id) {
callback(new Error(t('cityPlaceholder')))
}
if (areaList.district.length && !formData.district_id) {
callback(new Error(t('districtPlaceholder')))
}
callback()
}
}
],
address: [
{ required: true, message: t('addressPlaceholder'), trigger: 'blur' }
],
time_week: [
{ required: true, message: t('selectBusinessDays'), trigger: 'change' }
],
trade_time_json: [
{
validator: (rule: any, value: any, callback: any) => {
if (!value || value.length === 0) {
return callback(new Error(t('tradeTimePlaceholderTwo')))
}
for (let i = 0; i < value.length; i++) {
const timeRange = value[i]
if (!timeRange.start_time || !timeRange.end_time) {
return callback(new Error(t('tradeTimePlaceholderTwo')))
}
//
if (timeRange.end_time <= timeRange.start_time) {
return callback(new Error(t('tradeTimePlaceholderFour')))
}
//
if (i > 0 && value[i].start_time < value[i - 1].end_time) {
return callback(new Error(t('tradeTimePlaceholderFive')))
}
}
callback()
},
trigger: 'change',
required: true
}
],
time_interval: [
{ required: true, message: t('tradeTimePlaceholderThree'), trigger: 'change' }
]
}
})
/**
* 获取市
*/
watch(() => formData.province_id, (nval) => {
if (nval) {
getAreaListByPid(formData.province_id).then(res => {
areaList.city = res.data
const cityId = formData.city_id
if (cityId) {
let isExist = false
for (let i = 0; i < res.data.length; i++) {
if (cityId == res.data[i].id) {
isExist = true
break
}
}
if (isExist) {
formData.city_id = cityId
return
}
}
formData.city_id = 0
areaChange()
})
} else {
formData.city_id = 0
}
})
/**
* 获取区
*/
watch(() => formData.city_id, (nval) => {
if (nval) {
getAreaListByPid(formData.city_id).then(res => {
areaList.district = res.data
const districtId = formData.district_id
if (districtId) {
let isExist = false
for (let i = 0; i < res.data.length; i++) {
if (districtId == res.data[i].id) {
isExist = true
break
}
}
if (isExist) {
formData.district_id = districtId
return
}
}
areaChange()
formData.district_id = 0
})
} else {
formData.district_id = 0
}
})
watch(() => formData.district_id, (nval) => {
if (nval) {
areaChange()
}
})
const areaChange = debounce(() => {
setTimeout(() => {
const address = [
formData.province_id ? provinceRef.value.states.selectedLabel : '',
formData.city_id ? cityRef.value.states.selectedLabel : '',
formData.district_id ? districtRef.value.states.selectedLabel : '',
formData.address
]
addressToLatLng({ mapKey, address: address.join('') }).then(({ message, result }) => {
if (message == 'Success' || message == 'query ok') {
const latLng = new (window as any).TMap.LatLng(result.location.lat, result.location.lng)
map.setCenter(latLng)
marker.updateGeometries({
id: 'center',
position: latLng
})
formData.latitude = result.location.lat
formData.longitude = result.location.lng
} else {
console.error(message, result)
}
})
}, 500)
}, 500)
/**
* 地图点选获取市
*/
watch(() => storeArea.province_id, (nval) => {
if (nval) {
getAreaListByPid(storeArea.province_id).then(res => {
areaList.city = res.data
formData.province_id = storeArea.province_id
formData.city_id = storeArea.city_id
})
}
})
/**
* 地图点选获取区
*/
watch(() => storeArea.city_id, (nval) => {
if (nval) {
getAreaListByPid(storeArea.city_id).then(res => {
areaList.district = res.data
formData.city_id = storeArea.city_id
formData.district_id = storeArea.district_id
})
}
})
/**
* 地图点选获取区
*/
watch(() => storeArea.district_id, (nval) => {
if (nval) {
formData.district_id = storeArea.district_id
}
})
const onSave = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async(valid) => {
if (valid) {
loading.value = true
const data = cloneDeep(formData)
formData.province_name = formData.province_id ? provinceRef.value.states.selectedLabel : ''
formData.city_name = formData.city_id ? cityRef.value.states.selectedLabel : ''
formData.district_name = formData.district_id ? districtRef.value.states.selectedLabel : ''
const address = [
data.province_id ? provinceRef.value.states.selectedLabel : '',
data.city_id ? cityRef.value.states.selectedLabel : '',
data.district_id ? districtRef.value.states.selectedLabel : '',
data.address
]
data.full_address = address.join('')
// ****
data.trade_time_json = formData.trade_time_json.map(range => ({
start_time: range.start_time ? timeTransition(range.start_time) : null,
end_time: range.end_time ? timeTransition(range.end_time) : null
}))
const save = id ? editStore : addStore
save(data).then(res => {
loading.value = false
history.back()
}).catch(() => {
loading.value = false
})
}
})
}
const back = () => {
history.back()
}
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,144 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<el-page-header :content="pageName" :icon="ArrowLeft" @back="router.push('/shop/order/delivery')" />
</el-card>
<el-card class="box-card mt-[15px] !border-none" shadow="never">
<el-card class="box-card !border-none my-[10px] pt-0 table-search-wrap" shadow="never">
<div class="flex justify-between items-center">
<el-form :inline="true" :model="templateTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('templateName')" prop="template_name">
<el-input v-model.trim="templateTable.searchParam.template_name" :placeholder="t('templateNamePlaceholder')" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadTemplateList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
<el-button type="primary" @click="addEvent">
{{ t('addTemplate') }}
</el-button>
</div>
</el-card>
<div class="mt-[10px]">
<el-table :data="templateTable.data" size="large" v-loading="templateTable.loading">
<template #empty>
<span>{{ !templateTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column prop="template_name" :label="t('templateName')" min-width="120" />
<el-table-column prop="fee_type_name" :label="t('feeTypeName')" min-width="120" />
<el-table-column :label="t('freeShipping')" min-width="120" align="center">
<template #default="{ row }">
{{ row.is_free_shipping ? t('open') : t('close') }}
</template>
</el-table-column>
<el-table-column prop="create_time" :label="t('createTime')" min-width="120" />
<el-table-column :label="t('operation')" fixed="right" min-width="120" align="right">
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.template_id)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination v-model:current-page="templateTable.page" v-model:page-size="templateTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="templateTable.total"
@size-change="loadTemplateList()" @current-change="loadTemplateList" />
</div>
</div>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import { getShippingTemplatePageList, deleteShippingTemplate } from '@/addon/shop/api/delivery'
import { ElMessageBox, FormInstance } from 'element-plus'
import { ArrowLeft } from '@element-plus/icons-vue'
import { useRoute, useRouter } from 'vue-router'
import { setTablePageStorage,getTablePageStorage } from "@/utils/common";
const route = useRoute()
const router = useRouter()
const pageName = route.meta.title
const templateTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {
template_name: ''
}
})
const searchFormRef = ref<FormInstance>()
/**
* 获取运费模板列表
*/
const loadTemplateList = (page: number = 1) => {
templateTable.loading = true
templateTable.page = page
getShippingTemplatePageList({
page: templateTable.page,
limit: templateTable.limit,
...templateTable.searchParam
}).then(res => {
templateTable.loading = false
templateTable.data = res.data.data
templateTable.total = res.data.total
setTablePageStorage(templateTable.page, templateTable.limit, templateTable.searchParam);
}).catch(() => {
templateTable.loading = false
})
}
loadTemplateList(getTablePageStorage(templateTable.searchParam).page);
/**
* 添加运费模板
*/
const addEvent = () => {
router.push({ path: '/shop/order/shipping/template_edit' })
}
/**
* 编辑运费模板
* @param data
*/
const editEvent = (data: any) => {
router.push({ path: '/shop/order/shipping/template_edit', query: { id: data.template_id } })
}
/**
* 删除运费模板
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('templateDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}
).then(() => {
deleteShippingTemplate(id).then(() => {
loadTemplateList(getTablePageStorage(templateTable.searchParam).page);
}).catch(() => {
})
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadTemplateList()
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,505 @@
<template>
<div class="main-container">
<el-card class="card !border-none" shadow="never">
<el-page-header :content="pageName" :icon="ArrowLeft" @back="back" />
</el-card>
<el-card class="box-card mt-[15px] !border-none" shadow="never">
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
<el-form-item :label="t('templateName')" prop="template_name">
<el-input v-model.trim="formData.template_name" clearable :placeholder="t('templateNamePlaceholder')" class="input-width" maxlength="60" />
</el-form-item>
<el-form-item :label="t('feeTypeName')" prop="fee_type">
<el-radio-group v-model="formData.fee_type">
<el-radio label="num" size="large">{{ t('num') }}</el-radio>
<el-radio label="weight" size="large">{{ t('weight') }}</el-radio>
<el-radio label="volume" size="large">{{ t('volume') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('feeSetting')" prop="fee_data">
<el-table :data="feeData" style="width: 100%" size="default">
<el-table-column :label="t('deliveryArea')">
<template #default="{ row, $index }">
<div class="area-input">
<span v-if="$index" @click="selectArea('fee', $index)" class="cursor-pointer">{{ row.fee_area_names ? row.fee_area_names : t('areaPlaceholder') }}</span>
<span v-else>{{ row.fee_area_names ? row.fee_area_names : t('areaPlaceholder') }}</span>
</div>
</template>
</el-table-column>
<el-table-column :label="feeLabel.first">
<template #default="{ $index }">
<el-input v-model.trim="feeData[$index].snum" maxlength="8" @keyup="filterDigit($event)" @blur="feeData[$index].snum = $event.target.value"/>
</template>
</el-table-column>
<el-table-column :label="t('fee')">
<template #default="{ $index }">
<el-input v-model.trim="feeData[$index].sprice" maxlength="8" @keyup="filterDigit($event)" @blur="feeData[$index].sprice = $event.target.value"/>
</template>
</el-table-column>
<el-table-column :label="feeLabel.continue">
<template #default="{ $index }">
<el-input v-model.trim="feeData[$index].xnum" maxlength="8" @keyup="filterDigit($event)" @blur="feeData[$index].xnum = $event.target.value"/>
</template>
</el-table-column>
<el-table-column :label="t('continueFee')">
<template #default="{ $index }">
<el-input v-model.trim="feeData[$index].xprice" @keyup="filterDigit($event)" maxlength="8" @blur="feeData[$index].xprice = $event.target.value"/>
</template>
</el-table-column>
<el-table-column :label="t('operation')" align="right" width="150">
<template #default="{ $index }">
<el-button type="primary" @click="delArea('fee', $index)" link v-if="$index">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[10px]">
<el-button type="primary" @click="addArea('fee')">{{ t('addDeliveryArea') }}</el-button>
</div>
</el-form-item>
<!-- 指定区域包邮 -->
<el-form-item :label="t('freeShipping')" prop="is_free_shipping">
<el-switch v-model="formData.is_free_shipping" size="small" :inactive-value="0" :active-value="1" />
</el-form-item>
<el-form-item v-show="formData.is_free_shipping" prop="free_shipping_data">
<el-table :data="freeShippingData" style="width: 100%" size="default">
<el-table-column :label="t('freeShippingArea')">
<template #default="{ row, $index }">
<div class="area-input">
<el-input v-model.trim="row.free_shipping_area_names" :placeholder="t('areaPlaceholder')" readonly @click="selectArea('free_shipping', $index)" />
</div>
</template>
</el-table-column>
<el-table-column :label="freeShippingLabel">
<template #default="{ $index }">
<el-input v-model.trim="freeShippingData[$index].free_shipping_num" @keyup="filterDigit($event)" maxlength="8" @blur="freeShippingData[$index].free_shipping_num = $event.target.value"/>
</template>
</el-table-column>
<el-table-column :label="t('freeShippingPrice')">
<template #default="{ $index }">
<el-input v-model.trim="freeShippingData[$index].free_shipping_price" @keyup="filterDigit($event)" maxlength="8" @blur="freeShippingData[$index].free_shipping_price = $event.target.value"/>
</template>
</el-table-column>
<el-table-column :label="t('operation')" align="right" width="150">
<template #default="{ $index }">
<el-button type="primary" @click="delArea('free_shipping', $index)" link>{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="form-tip">{{ t('freeShippingAreaTips') }}</div>
<div class="mt-[10px]">
<el-button type="primary" @click="addArea('free_shipping')">{{ t('addFreeShippingArea') }}</el-button>
</div>
</el-form-item>
<!-- 指定区域不配送 -->
<el-form-item :label="t('noDelivery')" prop="no_delivery">
<el-switch v-model="formData.no_delivery" size="small" :inactive-value="0" :active-value="1" />
</el-form-item>
<el-form-item v-show="formData.no_delivery" prop="no_delivery_data">
<el-table :data="noDeliveryData" style="width: 100%" size="default">
<el-table-column :label="t('noDelivery')">
<template #default="{ row, $index }">
<div class="area-input">
<el-input v-model.trim="row.no_delivery_area_names" readonly @click="selectArea('no_delivery', $index)" :placeholder="t('areaPlaceholder')" />
</div>
</template>
</el-table-column>
<el-table-column :label="t('operation')" align="right" width="150">
<template #default="{ $index }">
<el-button type="primary" @click="delArea('no_delivery', $index)" link>{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[10px]">
<el-button type="primary" @click="addArea('no_delivery')">{{ t('addNoDelivery') }}</el-button>
</div>
</el-form-item>
</el-form>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="onSave(formRef)" :disabled="loading">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
</div>
<!-- 选择地区弹窗 -->
<el-dialog v-model="showSelectAreaDialog" :title="t('selectArea')" width="80%" class="diy-dialog-wrap" :destroy-on-close="true" @opened="showSelectOpened">
<el-scrollbar height="50vh">
<el-tree :data="areaTreeData" :props="{ children: 'child', label: 'name' }" default-expand-all show-checkbox ref="areaTreeRef" :default-checked-keys="selectedArea" node-key="id" />
</el-scrollbar>
<template #footer>
<span class="dialog-footer">
<el-button @click="showSelectAreaDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" :loading="loading" @click="confirmSelectArea">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, reactive, computed } from 'vue'
import { t } from '@/lang'
import { ElTree, FormInstance, ElMessage } from 'element-plus'
import { ArrowLeft } from '@element-plus/icons-vue'
import { addShippingTemplate, editShippingTemplate, getShippingTemplateInfo } from '@/addon/shop/api/delivery'
import { AnyObject } from '@/types/global'
import { useRoute, useRouter } from 'vue-router'
import { getAreatree } from '@/app/api/sys'
import { filterDigit } from '@/utils/common'
import Test from '@/utils/test'
const showSelectAreaDialog = ref(false)
const route = useRoute()
const router = useRouter()
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
template_id: '',
template_name: '',
fee_type: 'num',
area: [],
no_delivery: 0,
is_free_shipping: 0,
fee_data: [],
free_shipping_data: [],
no_delivery_data: []
}
const pageName = route.meta.title
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
const areaTree = ref<AnyObject[]>([])
if (route.query.id) {
loading.value = true
getShippingTemplateInfo(route.query.id).then(({ data }) => {
if (data) {
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
feeData.value = data.fee_data
noDeliveryData.value = data.no_delivery_data
freeShippingData.value = data.free_shipping_data
}
loading.value = false
}).catch(() => {
loading.value = false
})
}
getAreatree(2).then(res => {
areaTree.value = res.data
}).catch()
//
const formRules = computed(() => {
return {
template_name: [
{ required: true, message: t('templateNamePlaceholder'), trigger: 'blur' }
],
fee_data: [{ validator: feeDataValidate }],
free_shipping_data: [{ validator: freeShippingDataValidate }],
no_delivery_data: [{ validator: noDeliveryDataValidate }]
}
})
/**
* 运费模板运费数据校验
*/
const feeDataValidate = (rule: any, value: any, callback: any) => {
for (let i = 0; i < feeData.value.length; i++) {
const item = feeData.value[i]
if (!item.area_ids.length) {
callback(new Error(t('areaPlaceholder')))
break
}
if (Test.empty(item.snum) || item.snum < 0) {
callback(new Error(feeLabel.value.first + t('notUnderZero')))
break
}
if (Test.empty(item.xnum) || item.snum < 0) {
callback(new Error(feeLabel.value.continue + t('notUnderZero')))
break
}
}
callback()
}
/**
* 运费模板包邮数据校验
*/
const freeShippingDataValidate = (rule: any, value: any, callback: any) => {
if (formData.is_free_shipping) {
for (let i = 0; i < freeShippingData.value.length; i++) {
const item = freeShippingData.value[i]
if (!item.area_ids.length) {
callback(new Error(t('freeShippingPlaceholder')))
break
}
if (Test.empty(item.free_shipping_num) || item.free_shipping_num < 0) {
callback(new Error(freeShippingLabel.value + t('notUnderZero')))
break
}
}
callback()
} else {
callback()
}
}
/**
* 运费模板不配送地区校验
*/
const noDeliveryDataValidate = (rule: any, value: any, callback: any) => {
if (formData.no_delivery) {
for (let i = 0; i < noDeliveryData.value.length; i++) {
const item = noDeliveryData.value[i]
if (!item.area_ids.length) {
callback(new Error(t('noDeliveryPlaceholder')))
break
}
}
callback()
} else {
callback()
}
}
const feeLabel = computed(() => {
const label: AnyObject = {
num: {
first: t('firstNum'),
continue: t('continueNum')
},
weight: {
first: t('firstWeight'),
continue: t('continueWeight')
},
volume: {
first: t('firstVolume'),
continue: t('continueVolume')
}
}
return label[formData.fee_type]
})
const freeShippingLabel = computed(() => {
const label: AnyObject = {
num: t('freeShippingNum'),
weight: t('freeShippingWeight'),
volume: t('freeShippingVolume')
}
return label[formData.fee_type]
})
//
const feeData = ref<AnyObject[]>([
{ area_ids: [0], fee_area_names: '全国', snum: 1, sprice: 0, xnum: 1, xprice: 0 }
])
//
const freeShippingData = ref<AnyObject[]>([])
//
const noDeliveryData = ref<AnyObject[]>([])
/**
* 添加地区
* @param type
*/
const addArea = (type: string) => {
switch (type) {
case 'fee':
feeData.value.push({ area_ids: [], fee_area_names: '', snum: 1, sprice: 0, xnum: 1, xprice: 0 })
break
case 'free_shipping':
freeShippingData.value.push({ area_ids: [], free_shipping_area_names: '', free_shipping_num: 0, free_shipping_price: 0 })
break
case 'no_delivery':
noDeliveryData.value.push({ area_ids: [], no_delivery_area_names: '' })
break
}
}
/**
*删除地区
* @param type
* @param index
*/
const delArea = (type: string, index: number) => {
switch (type) {
case 'fee':
feeData.value.splice(index, 1)
break
case 'free_shipping':
freeShippingData.value.splice(index, 1)
break
case 'no_delivery':
noDeliveryData.value.splice(index, 1)
break
}
}
//
let selectedArea: number[] = []
//
const disabledArea = ref <Array<string|number>>([])
let currSelect = { type: '', index: 0 }
const selectArea = (type: string, index: number) => {
currSelect = { type, index }
let data: AnyObject[] = []
switch (type) {
case 'fee':
data = feeData.value
break
case 'free_shipping':
data = freeShippingData.value
break
case 'no_delivery':
data = noDeliveryData.value
break
}
selectedArea = data[index].area_ids
disabledArea.value = []
data.forEach((item, $index) => {
if (index != $index) disabledArea.value.push(...item.area_ids)
})
showSelectAreaDialog.value = true
}
const areaTreeData = computed(() => {
areaTree.value.forEach(province => {
// province child undefined.child
if (province && Array.isArray(province.child)) {
province.child.forEach((city: any) => {
city.disabled = disabledArea.value?.includes(city.id) || false;
});
}
})
return areaTree.value
})
const areaTreeRef = ref<InstanceType<typeof ElTree>>()
const confirmSelectArea = () => {
const nodes = areaTreeRef.value!.getCheckedNodes(false, false)
const areaIds: number[] = []
const areaNames: string[] = []
nodes.forEach(item => {
if (item.level == 2) {
areaIds.push(item.id)
areaNames.push(item.name)
}
})
switch (currSelect.type) {
case 'fee':
feeData.value[currSelect.index].area_ids = areaIds
feeData.value[currSelect.index].fee_area_names = areaNames.toString()
break
case 'free_shipping':
freeShippingData.value[currSelect.index].area_ids = areaIds
freeShippingData.value[currSelect.index].free_shipping_area_names = areaNames.toString()
break
case 'no_delivery':
noDeliveryData.value[currSelect.index].area_ids = areaIds
noDeliveryData.value[currSelect.index].no_delivery_area_names = areaNames.toString()
break
}
showSelectAreaDialog.value = false
}
const showSelectOpened = () => {
areaTreeRef.value!.setCheckedKeys(selectedArea, false)
}
/**
* 确认
* @param formEl
*/
const onSave = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
const save = formData.template_id ? editShippingTemplate : addShippingTemplate
await formEl.validate(async (valid) => {
if (valid) {
if (formData.is_free_shipping && freeShippingData.value.length == 0) {
ElMessage.error(t('freeShippingPlaceholder'))
return
}
if (formData.no_delivery && noDeliveryData.value.length == 0) {
ElMessage.error('noDeliveryPlaceholder')
return
}
loading.value = true
const data:AnyObject = {
template_id: formData.template_id,
template_name: formData.template_name,
fee_type: formData.fee_type,
no_delivery: formData.no_delivery,
is_free_shipping: formData.is_free_shipping
}
const area: AnyObject = {}
feeData.value.forEach(item => {
item.area_ids.forEach((city: number) => {
area['city_' + city] = { city_id: city, fee_area_ids: item.area_ids.toString(), fee_area_names: item.fee_area_names, snum: item.snum, sprice: item.sprice, xnum: item.xnum, xprice: item.xprice }
})
})
freeShippingData.value.forEach(item => {
item.area_ids.forEach((city: number) => {
if (area['city_' + city]) {
Object.assign(area['city_' + city], { free_shipping_area_ids: item.area_ids.toString(), free_shipping_area_names: item.free_shipping_area_names, free_shipping_num: item.free_shipping_num, free_shipping_price: item.free_shipping_price })
} else {
area['city_' + city] = { city_id: city, free_shipping_area_ids: item.area_ids.toString(), free_shipping_area_names: item.free_shipping_area_names, free_shipping_num: item.free_shipping_num, free_shipping_price: item.free_shipping_price }
}
})
})
noDeliveryData.value.forEach(item => {
item.area_ids.forEach((city: number) => {
if (area['city_' + city]) {
Object.assign(area['city_' + city], { no_delivery_area_ids: item.area_ids.toString(), no_delivery_area_names: item.no_delivery_area_names })
} else {
area['city_' + city] = { city_id: city, no_delivery_area_ids: item.area_ids.toString(), no_delivery_area_names: item.no_delivery_area_names }
}
})
})
data.area = Object.values(area)
save(data).then(() => {
loading.value = false
router.push({ path: '/shop/order/shipping/template' })
}).catch(() => {
loading.value = false
})
}
})
}
const back = () => {
router.push({ path: '/shop/order/shipping/template' })
}
</script>
<style lang="scss" scoped>
:deep(.el-tree-node.is-expanded>.el-tree-node__children) {
display: flex !important;
flex-wrap: wrap;
}
:deep(.area-input .el-input__wrapper) {
box-shadow: none !important;
padding: 0 !important;
background: none;
input {
cursor: pointer;
}
}
</style>

View File

@ -0,0 +1,254 @@
<template>
<!-- 内容 -->
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('selectStyle') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('selectStyle')" class="flex">
<span class="text-primary flex-1 cursor-pointer"
@click="showCouponStyle">{{ diyStore.editComponent.styleName }}</span>
<el-icon @click="showCouponStyle" class="cursor-pointer">
<ArrowRight />
</el-icon>
</el-form-item>
</el-form>
<el-dialog v-model="showCouponDialog" :title="t('selectStyle')" width="500px">
<div class="flex flex-wrap">
<template v-for="(item,index) in couponStyleList" :key="index">
<div :class="{ 'border-primary': selectCouponStyle.value == item.value }"
@click="changeCouponStyle(item)"
class="flex items-center justify-center overflow-hidden w-[200px] h-[100px] m-[6px] cursor-pointer border bg-gray-50">
<img :src="img(item.url)" />
</div>
</template>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="showCouponDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" @click="confirmCouponStyle">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('couponContent') }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item :label="t('couponTitle')">
<el-input v-model.trim="diyStore.editComponent.couponTitle" clearable :maxlength="diyStore.editComponent.style == 'style-3' ? 4 : 8" show-word-limit />
</el-form-item>
<el-form-item :label="t('couponSubTitle')">
<el-input v-model.trim="diyStore.editComponent.couponSubTitle" clearable :maxlength="diyStore.editComponent.style == 'style-3' ? 7 : 10" show-word-limit />
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('couponData') }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item :label="t('selectCoupon')">
<el-radio-group v-model="diyStore.editComponent.source" :title="t('goodsSelectPopupSelectGoodsButton')">
<el-radio label="all">{{ t('allSources') }}</el-radio>
<el-radio label="custom">{{ t('manualSelectionSources') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('manualSelectionSources')" v-if="diyStore.editComponent.source == 'custom'">
<coupon-select-popup ref="couponSelectPopupRef" v-model="diyStore.editComponent.couponIds" :min="1" :max="20" />
</el-form-item>
<el-form-item :label="t('couponNum')" v-if="diyStore.editComponent.source == 'all'">
<el-slider show-input v-model="diyStore.editComponent.num" :min="1" max="20" size="small"
class="goods-coupon-slider" />
</el-form-item>
<el-form-item :label="t('couponBtnText')" v-if="diyStore.editComponent.style != 'style-4'">
<el-input v-model.trim="diyStore.editComponent.btnText" clearable maxlength="5" show-word-limit />
</el-form-item>
</el-form>
</div>
</div>
<!-- 样式 -->
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
<div class="edit-attr-item-wrap" v-if="diyStore.editComponent.style == 'style-4'">
<h3 class="mb-[10px]">{{ t("couponTitleStyle") }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item :label="t('couponTitleColor')">
<el-color-picker v-model="diyStore.editComponent.titleColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('couponSubTitleColor')">
<el-color-picker v-model="diyStore.editComponent.subTitleColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap" v-if="diyStore.editComponent.style == 'style-4'">
<h3 class="mb-[10px]">{{ t("couponItemStyle") }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item :label="t('couponMoney')">
<el-color-picker v-model="diyStore.editComponent.couponItem.moneyColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('textColor')">
<el-color-picker v-model="diyStore.editComponent.couponItem.textColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('subTextColor')">
<el-color-picker v-model="diyStore.editComponent.couponItem.subTextColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('listFrameColor')">
<el-color-picker v-model="diyStore.editComponent.couponItem.bgColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('goodsRounded')">
<el-slider v-model="diyStore.editComponent.couponItem.aroundRadius" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
</el-form>
</div>
<!-- 组件样式 -->
<slot name="style"></slot>
</div>
</template>
<script lang="ts" setup>
import { t } from '@/lang'
import useDiyStore from '@/stores/modules/diy'
import { img } from '@/utils/common'
import { ref, reactive } from 'vue'
import couponSelectPopup from '@/addon/shop/views/goods/components/coupon-select-popup.vue'
const diyStore = useDiyStore()
diyStore.editComponent.ignore = ['componentBgColor', 'componentBgUrl'] //
//
diyStore.editComponent.verify = (index: number) => {
const res = { code: true, message: '' }
if (diyStore.value[index].source == 'custom') {
if (diyStore.value[index].couponIds.length == 0) {
res.code = false
res.message = t('couponPlaceholder')
return res
}
}
if (diyStore.value[index].btnText == '') {
res.code = false
res.message = t('couponBtnTextPlaceholder')
return res
}
if (diyStore.value[index].couponTitle == '') {
res.code = false
res.message = t('couponTitlePlaceholder')
return res
}
if (diyStore.value[index].couponSubTitle == '') {
res.code = false
res.message = t('couponSubTitlePlaceholder')
return res
}
return res
}
const selectCouponStyle = reactive({
title: diyStore.editComponent.styleName,
value: diyStore.editComponent.style
})
//
const showCouponDialog = ref(false)
const showCouponStyle = () => {
showCouponDialog.value = true
selectCouponStyle.title = diyStore.editComponent.styleName
selectCouponStyle.value = diyStore.editComponent.style
}
const couponStyleList = reactive([
{
url: 'addon/shop/diy/goods_coupon/style-1.png',
title: '风格1',
value: 'style-1'
},
{
url: 'addon/shop/diy/goods_coupon/style-2.png',
title: '风格2',
value: 'style-2'
},
{
url: 'addon/shop/diy/goods_coupon/style-3.png',
title: '风格3',
value: 'style-3'
},
{
url: 'addon/shop/diy/goods_coupon/style-4.png',
title: '风格4',
value: 'style-4'
}
])
const changeCouponStyle = (item: any) => {
selectCouponStyle.title = item.title
selectCouponStyle.value = item.value
}
const confirmCouponStyle = () => {
diyStore.editComponent.styleName = selectCouponStyle.title
diyStore.editComponent.style = selectCouponStyle.value
if (diyStore.editComponent.style == 'style-3') {
if (diyStore.editComponent.couponTitle && diyStore.editComponent.couponTitle.length > 4) {
diyStore.editComponent.couponTitle = diyStore.editComponent.couponTitle.substring(0, 4)
}
if (diyStore.editComponent.couponSubTitle && diyStore.editComponent.couponSubTitle.length > 7) {
diyStore.editComponent.couponSubTitle = diyStore.editComponent.couponSubTitle.substring(0, 7)
}
}
initStyleFn()
showCouponDialog.value = false
}
const initStyleFn = () => {
const index = diyStore.editComponent.ignore.indexOf('componentBgColor')
if (diyStore.editComponent.style == 'style-4' && index != -1) {
diyStore.editComponent.ignore.splice(index, 1)
diyStore.editComponent.titleColor = '#ffffff'
diyStore.editComponent.subTitleColor = '#ffffff'
diyStore.editComponent.couponItem.moneyColor = '#fa191d'
diyStore.editComponent.couponItem.textColor = '#333333'
diyStore.editComponent.couponItem.subTextColor = '#999999'
diyStore.editComponent.couponItem.bgColor = '#ffffff'
diyStore.editComponent.couponItem.aroundRadius = 10
diyStore.editComponent.componentStartBgColor = '#fa191d'
} else if (diyStore.editComponent.style != 'style-4' && index == -1) {
diyStore.editComponent.ignore.push('componentBgColor')
}
}
defineExpose({})
</script>
<style lang="scss" scoped>
</style>
<style lang="scss">
.goods-coupon-slider {
.el-slider__input {
width: 100px;
}
}
</style>

View File

@ -0,0 +1,370 @@
<template>
<!-- 内容 -->
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('selectStyle') }}</h3>
<div class="flex items-center mb-[18px] rounded overflow-hidden">
<span
class="iconfont iconzuoyoutuwenpc border-[1px] border-solid border-[#eee] cursor-pointer flex-1 flex items-center justify-center py-[5px]"
:class="{ 'border-[var(--el-color-primary)] text-[var(--el-color-primary)]': diyStore.editComponent.style == 'style-1' }"
@click="styleChangeFn('style-1')"></span>
<span
class="iconfont iconshangxiatuwenpc border-[1px] border-solid border-[#eee] cursor-pointer flex-1 flex items-center justify-center py-[5px]"
:class="{ 'border-[var(--el-color-primary)] text-[var(--el-color-primary)]': diyStore.editComponent.style == 'style-2' }"
@click="styleChangeFn('style-2')"></span>
<span
class="iconfont iconliebiaopc border-[1px] border-solid border-[#eee] cursor-pointer flex-1 flex items-center justify-center py-[5px]"
:class="{ 'border-[var(--el-color-primary)] text-[var(--el-color-primary)]': diyStore.editComponent.style == 'style-3' }"
@click="styleChangeFn('style-3')"></span>
</div>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t("selectSource") }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('sortWay')">
<el-radio-group v-model="diyStore.editComponent.sortWay">
<el-radio label="default">{{ t('default') }}</el-radio>
<el-radio label="sale_num">{{ t('sales') }}</el-radio>
<el-radio label="price">{{ t('price') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('goodsSelectPopupSelectGoodsButton')">
<el-radio-group v-model="diyStore.editComponent.source" :title="t('goodsSelectPopupSelectGoodsButton')">
<el-radio label="all">{{ t('goodsSelectPopupAllGoods') }}</el-radio>
<el-radio label="category">{{ t('selectCategory') }}</el-radio>
<el-radio label="custom">{{ t('manualSelectionSources') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('selectCategory')" v-if="diyStore.editComponent.source == 'category'">
<div class="flex items-center w-full">
<div class="cursor-pointer ml-auto" @click="categoryShowDialogOpen">
<span class="text-[var(--el-color-primary)]">{{ diyStore.editComponent.goods_category_name }}</span>
<span class="iconfont iconxiangyoujiantou"></span>
</div>
</div>
</el-form-item>
<el-form-item :label="t('goodsNum')" v-if="diyStore.editComponent.source == 'all' || diyStore.editComponent.source == 'category'">
<el-slider class="goods-list-slider" show-input v-model="diyStore.editComponent.num" :min="1" max="20" size="small" />
</el-form-item>
<el-form-item :label="t('customGoods')" v-if="diyStore.editComponent.source == 'custom'">
<goods-select-popup ref="goodsSelectPopupRef" v-model="diyStore.editComponent.goods_ids" :min="1" :max="99" />
</el-form-item>
</el-form>
<el-dialog v-model="categoryShowDialog" :title="t('goodsCategoryTitle')" width="750px" :destroy-on-close="true" :close-on-click-modal="false">
<el-table :data="categoryTable.data" ref="categoryTableRef" size="large"
v-loading="categoryTable.loading"
height="450px" @selection-change="handleSelectionChange" row-key="category_id"
:expand-row-keys="expand_category_ids"
:tree-props="{ hasChildren: 'hasChildren', children: 'child_list' }">
<template #empty>
<span>{{ !categoryTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column type="selection" width="55" />
<el-table-column :label="t('categoryName')" min-width="120">
<template #default="{ row }">
<span class="order-2">{{ row.category_name }}</span>
</template>
</el-table-column>
<el-table-column :label="t('categoryImage')" width="170" align="left">
<template #default="{ row }">
<div class="h-[30px]">
<el-image class="w-[30px] h-[30px] " :src="img(row.image)" fit="contain">
<template #error>
<div class="image-slot">
<img class="w-[30px] h-[30px]" src="@/addon/shop/assets/category_default.png" />
</div>
</template>
</el-image>
</div>
</template>
</el-table-column>
</el-table>
<div class="flex items-center justify-end mt-[15px]">
<el-button type="primary" @click="saveCategoryId">{{ t('confirm') }}</el-button>
<el-button @click="categoryShowDialog = false">{{ t('cancel') }}</el-button>
</div>
</el-dialog>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t("goodsBuyBtn") }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item :label="t('goodsBtnIsShow')">
<el-switch v-model="diyStore.editComponent.btnStyle.control" />
</el-form-item>
<el-form-item :label="t('goodsCartIncident')" v-if="diyStore.editComponent.btnStyle.control">
<el-radio-group v-model="diyStore.editComponent.btnStyle.cartEvent">
<el-radio label="detail">{{ t('goodsDetail') }}</el-radio>
<el-radio v-if="diyStore.editComponent.style != 'style-3'" label="cart">{{ t('goodsAddCart') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('goodsBtnStyle')" class="!items-center" v-if="diyStore.editComponent.btnStyle.control">
<div class="flex">
<template v-for="(item,index) in btnStyleList">
<div v-if=" item.isShow == true"
class="cursor-pointer flex items-center justify-center border-[1px] border-solid border-transparent rounded-[6px] py-[5px] px-[8px] mr-[10px]"
:class="{'!border-[var(--el-color-primary)]': diyStore.editComponent.btnStyle.style == item.value}">
<div v-if="item.type == 'icon'"
:class="['nc-iconfont !text-[25px] text-[var(--el-color-primary)]', item.title]" @click="changeBtnStyle(item)"></div>
<div v-if="item.type == 'button'"
class="leading-[1] text-[12px] px-[10px] py-[8px] text-[#fff] rounded-[20px] bg-[var(--el-color-primary)]"
@click="changeBtnStyle(item)">{{ item.title }}</div>
</div>
</template>
</div>
</el-form-item>
<el-form-item :label="t('goodsBtnText')"
v-if="diyStore.editComponent.btnStyle.control && diyStore.editComponent.btnStyle.style == 'button'">
<el-input v-model.trim="diyStore.editComponent.btnStyle.text"
:placeholder="t('goodsBtnTextPlaceholder')" clearable maxlength="4" show-word-limit />
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t("goodsShowContent") }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item :label="t('goodsSelectPopupGoodsName')" v-if="diyStore.editComponent.goodsNameStyle.isShow">
<el-switch v-model="diyStore.editComponent.goodsNameStyle.control" />
</el-form-item>
<el-form-item :label="t('goodsPriceColor')" v-if="diyStore.editComponent.priceStyle.isShow">
<el-switch v-model="diyStore.editComponent.priceStyle.control" />
</el-form-item>
<el-form-item :label="t('goodsSaleNum')" v-if="diyStore.editComponent.saleStyle.isShow">
<el-switch v-model="diyStore.editComponent.saleStyle.control" />
</el-form-item>
<el-form-item :label="t('goodsLabel')" v-if="diyStore.editComponent.labelStyle.isShow">
<el-switch v-model="diyStore.editComponent.labelStyle.control" />
</el-form-item>
</el-form>
</div>
</div>
<!-- 样式 -->
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('goodsStyle') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('goodsBgColor')">
<el-color-picker v-model="diyStore.editComponent.elementBgColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('goodsNameColor')">
<el-color-picker v-model="diyStore.editComponent.goodsNameStyle.color" show-alpha :predefine="diyStore.predefineColors" />
<div class="mr-[20px]"></div>
<el-radio-group v-model="diyStore.editComponent.goodsNameStyle.fontWeight">
<el-radio :label="'normal'">{{ t('fontWeightNormal') }}</el-radio>
<el-radio :label="'bold'">{{ t('fontWeightBold') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('imageRounded')">
<el-slider v-model="diyStore.editComponent.imgElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
<el-form-item :label="t('goodsPriceColor')">
<el-color-picker v-model="diyStore.editComponent.priceStyle.color" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('goodsSaleColor')">
<el-color-picker v-model="diyStore.editComponent.saleStyle.color" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('topRounded')">
<el-slider v-model="diyStore.editComponent.topElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
<el-form-item :label="t('bottomRounded')">
<el-slider v-model="diyStore.editComponent.bottomElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap" v-if="diyStore.editComponent.btnStyle.control">
<h3 class="mb-[10px]">{{ t("goodsBuyBtn") }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('goodsIsBold')" v-if="diyStore.editComponent.btnStyle.style == 'button'">
<el-switch v-model="diyStore.editComponent.btnStyle.fontWeight" />
</el-form-item>
<el-form-item :label="t('goodsTextColor')">
<el-color-picker v-model="diyStore.editComponent.btnStyle.textColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('listFrameColor')">
<el-color-picker v-model="diyStore.editComponent.btnStyle.startBgColor" show-alpha :predefine="diyStore.predefineColors" />
<icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]" />
<el-color-picker v-model="diyStore.editComponent.btnStyle.endBgColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('goodsRounded')" v-if="diyStore.editComponent.btnStyle.style == 'button'">
<el-slider v-model="diyStore.editComponent.btnStyle.aroundRadius" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
</el-form>
</div>
<!-- 组件样式 -->
<slot name="style"></slot>
</div>
</template>
<script lang="ts" setup>
import { getCategoryTree } from '@/addon/shop/api/goods'
import { t } from '@/lang'
import { img } from '@/utils/common'
import useDiyStore from '@/stores/modules/diy'
import { ref, reactive, onMounted, nextTick } from 'vue'
import { ElTable } from 'element-plus'
import goodsSelectPopup from '@/addon/shop/views/goods/components/goods-select-popup.vue'
const diyStore: any = useDiyStore()
diyStore.editComponent.ignore = [] //
//
diyStore.editComponent.verify = (index: number) => {
const res = { code: true, message: '' }
if (diyStore.value[index].source == 'category') {
if (diyStore.value[index].goods_category == '') {
res.code = false
res.message = t('goodsCategoryPlaceholder')
}
} else if (diyStore.value[index].source == 'custom') {
if (diyStore.value[index].goods_ids.length == 0) {
res.code = false
res.message = t('goodsPlaceholder')
}
}
return res
}
const categoryShowDialog = ref(false)
const categoryTable = reactive({
loading: true,
data: []
})
onMounted(() => {
loadCategoryList()
btnStyleList.forEach((item, index, arr) => {
if (item.type == 'button') {
if (diyStore.editComponent.style == 'style-3') {
item.isShow = false
} else {
item.isShow = true
}
}
})
})
const styleChangeFn = (style) => {
btnStyleList.forEach((item, index, arr) => {
if (item.type == 'button') {
if (style == 'style-3') {
item.isShow = false
} else {
item.isShow = true
}
}
})
if (style == 'style-3') {
diyStore.editComponent.btnStyle.style = btnStyleList[1].value
diyStore.editComponent.btnStyle.cartEvent = 'detail'
diyStore.editComponent.saleStyle.isShow = false
diyStore.editComponent.labelStyle.isShow = false
} else {
diyStore.editComponent.btnStyle.style = btnStyleList[0].value
diyStore.editComponent.saleStyle.isShow = true
diyStore.editComponent.labelStyle.isShow = true
}
diyStore.editComponent.style = style
}
const btnStyleList = reactive([
{
isShow: true,
type: 'button',
title: diyStore.editComponent.btnStyle.text,
value: 'button'
},
{
isShow: true,
type: 'icon',
title: 'nc-icon-jiahaoV6xx',
value: 'nc-icon-jiahaoV6xx'
},
{
isShow: true,
type: 'icon',
title: 'nc-icon-gouwuche1',
value: 'nc-icon-gouwuche1'
}
])
const changeBtnStyle = (item: any) => {
diyStore.editComponent.btnStyle.style = item.value
}
const categoryTableRef = ref<InstanceType<typeof ElTable>>()
/**
* 获取商品分类列表
*/
let currCategoryData: any = null
const loadCategoryList = () => {
categoryTable.loading = true
getCategoryTree().then(res => {
categoryTable.loading = false
categoryTable.data = res.data
}).catch(() => {
categoryTable.loading = false
})
}
//
const handleSelectionChange = (val: string | any[]) => {
let data = ''
if (val) data = val[val.length - 1]
if (val.length > 1) categoryTableRef.value!.clearSelection()
if (data) categoryTableRef.value!.toggleRowSelection(data, true)
currCategoryData = data
}
const saveCategoryId = () => {
diyStore.editComponent.goods_category = currCategoryData.category_id
diyStore.editComponent.goods_category_name = currCategoryData.category_name
categoryShowDialog.value = false
}
const categoryShowDialogOpen = () => {
categoryShowDialog.value = true
nextTick(() => {
setRowSelection()
})
}
// ,
const expand_category_ids = ref<Array<any>>([])
const setRowSelection = () => {
expand_category_ids.value = []
categoryTable.data.forEach((el: any) => {
if (diyStore.editComponent.goods_category == el.category_id) {
categoryTableRef.value!.toggleRowSelection(el, true)
} else if (el.child_list && el.child_list.length) {
el.child_list.forEach((v: any) => {
if (diyStore.editComponent.goods_category == v.category_id) {
expand_category_ids.value.push(el.category_id.toString())
categoryTableRef.value!.toggleRowSelection(v, true)
}
})
}
})
}
defineExpose({})
</script>
<style lang="scss">
.goods-list-slider {
.el-slider__input {
width: 100px;
}
}
</style>

View File

@ -0,0 +1,654 @@
<template>
<!-- 内容 -->
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('selectStyle') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('styleLabel')">
<el-radio-group v-model="diyStore.editComponent.headStyle">
<el-radio label="style-1">{{ t('headStyle1') }}</el-radio>
<el-radio label="style-2">{{ t('headStyle2') }}</el-radio>
<el-radio label="style-3">{{ t('headStyle3') }}</el-radio>
<el-radio label="style-4">{{ t('headStyle4') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('manyGoodsListAroundRadius')" v-show="diyStore.editComponent.headStyle == 'style-3'">
<el-slider v-model="diyStore.editComponent.aroundRadius" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('goodsSet') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('sortWay')">
<el-radio-group v-model="diyStore.editComponent.sortWay">
<el-radio label="default">{{ t('default') }}</el-radio>
<el-radio label="sale_num">{{ t('sales') }}</el-radio>
<el-radio label="price">{{ t('price') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('goodsNum')">
<el-slider show-input class="diy-nav-slider" v-model="diyStore.editComponent.num" :min="1" max="20" size="small" />
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t("manyGoodsListCategorySet") }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('dataSources')">
<el-radio-group v-model="diyStore.editComponent.source">
<el-radio label="custom">{{ t('manyGoodsListSourceDiy') }}</el-radio>
<el-radio label="goods_category">{{ t('manyGoodsListSourceCategory') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('goodsCategoryTitle')" v-show="diyStore.editComponent.source == 'goods_category'">
<el-input v-model.trim="diyStore.editComponent.goods_category_name" :placeholder="t('selectCategory')" readonly class="select-diy-page-input" @click="firstCategoryShowDialogOpen()">
<template #suffix>
<div @click.stop="clearCategory">
<el-icon v-if="diyStore.editComponent.goods_category_name">
<Close />
</el-icon>
<el-icon v-else>
<ArrowRight />
</el-icon>
</div>
</template>
</el-input>
</el-form-item>
<div v-show="diyStore.editComponent.source == 'custom'">
<p class="text-sm text-gray-400 mb-[10px]">{{ t('dragMouseAdjustOrder') }}</p>
<div ref="goodsBoxRef">
<div v-for="(item,index) in diyStore.editComponent.list" :key="item.id"
class="item-wrap p-[10px] pb-0 relative border border-dashed border-gray-300 mb-[16px]">
<el-form-item :label="t('manyGoodsListCategoryName')">
<el-input v-model.trim="item.title" clearable maxlength="4" show-word-limit />
</el-form-item>
<el-form-item :label="t('manyGoodsListSubTitle')" v-show="diyStore.editComponent.headStyle == 'style-1'">
<el-input v-model.trim="item.desc" clearable maxlength="5" show-word-limit />
</el-form-item>
<el-form-item :label="t('goodsSelectPopupSelectGoodsButton')">
<el-radio-group v-model="item.source">
<el-radio label="all">{{ t('goodsSelectPopupAllGoods') }}</el-radio>
<el-radio label="category">{{ t('selectCategory') }}</el-radio>
<el-radio label="custom">{{ t('manualSelectionSources') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('selectCategory')" v-if="item.source == 'category'">
<div class="flex items-center w-full">
<div class="cursor-pointer ml-auto" @click="categoryShowDialogOpen(index)">
<span class="text-[var(--el-color-primary)]">{{ item.goods_category_name }}</span>
<span class="iconfont iconxiangyoujiantou"></span>
</div>
</div>
</el-form-item>
<el-form-item :label="t('customGoods')" v-if="item.source == 'custom'">
<goods-select-popup ref="goodsSelectPopupRef" v-model="item.goods_ids" :min="1" :max="99" />
</el-form-item>
<el-form-item :label="t('image')" v-show="diyStore.editComponent.headStyle == 'style-3'">
<upload-image v-model="item.imageUrl" :limit="1" />
</el-form-item>
<div class="del absolute cursor-pointer z-[2] top-[-8px] right-[-8px]"
v-show="diyStore.editComponent.list.length > 1"
@click="diyStore.editComponent.list.splice(index,1)">
<icon name="element CircleCloseFilled" color="#bbb" size="20px" />
</div>
</div>
</div>
<el-button class="w-full" @click="addItem">{{ t('manyGoodsLisAddItem') }}</el-button>
</div>
</el-form>
<!-- 选择一级商品分类弹出框 -->
<el-dialog v-model="firstCategoryShowDialog" :title="t('goodsCategoryTitle')" width="750px" :destroy-on-close="true" :close-on-click-modal="false">
<el-table :data="firstCategoryTable.data" ref="firstCategoryTableRef" size="large"
v-loading="firstCategoryTable.loading" height="450px"
@current-change="handleCurrentCategoryChange" row-key="category_id" highlight-current-row>
<template #empty>
<span>{{ !firstCategoryTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column :label="t('categoryName')" min-width="120">
<template #default="{ row }">
<span class="order-2">{{ row.category_name }}</span>
</template>
</el-table-column>
<el-table-column :label="t('categoryImage')" width="170" align="left">
<template #default="{ row }">
<div class="h-[30px]">
<el-image class="w-[30px] h-[30px] " :src="img(row.image)" fit="contain">
<template #error>
<div class="image-slot">
<img class="w-[30px] h-[30px]" src="@/addon/shop/assets/category_default.png" />
</div>
</template>
</el-image>
</div>
</template>
</el-table-column>
</el-table>
<div class="flex items-center justify-end mt-[15px]">
<el-button type="primary" @click="saveFirstCategoryId">{{ t('confirm') }}</el-button>
<el-button @click="firstCategoryShowDialog = false">{{ t('cancel') }}</el-button>
</div>
</el-dialog>
<el-dialog v-model="categoryShowDialog" :title="t('goodsCategoryTitle')" width="750px" :destroy-on-close="true" :close-on-click-modal="false">
<div class="table w-[100%] mt-[15px]" v-loading="categoryTable.loading">
<div class="table-head flex items-center bg-[#f5f7f9] py-[10px] text-[14px]" :style="{ paddingRight: scrollBarWidth + 'px' }">
<div class="w-[6%]"></div>
<div class="w-[10%]">
<!-- <el-checkbox v-model="staircheckAll" :indeterminate="isStairIndeterminate" @change="handleCheckAllChange" /> -->
</div>
<div class="w-[50%]">{{ t('categoryName') }}</div>
<div class="w-[34%] h-[30px] leading-[30px]">{{ t('categoryImage') }}</div>
</div>
<div class="table-body max-h-[500px] overflow-y-auto" ref="tableBodyRef">
<!-- 遍历一级分类 -->
<div v-for="(row, rowIndex) in categoryTable.data" :key="rowIndex" class="flex flex-col">
<div class="flex items-center border-solid border-[#e5e7eb] py-[10px] border-b-[1px]">
<!-- 图标展开/收起子级 -->
<div v-if="row.child_list && row.child_list.length" class="w-[6%] cursor-pointer text-center !text-[10px]" @click="secondLevelArrowChange(row)" :class="{ 'iconfont iconxiangyoujiantou': row.child_list.length, 'arrow-show': row.isShow }"></div>
<div v-else class="w-[6%]"></div>
<!-- 一级分类复选框 -->
<div class="w-[10%]">
<el-checkbox v-model="row.secondLevelCheckAll" :indeterminate="row.isSecondLevelIndeterminate" @change="handleCheckboxChange($event, row)" />
</div>
<!-- 一级分类名称 -->
<div class="ml-2 flex flex-col items-start w-[50%]">
<span :title="row.category_name" class="multi-hidden leading-[1.4] mr-5 text-[14px] text-[#666]">
{{ row.category_name }}
</span>
</div>
<!-- 一级分类图片 -->
<div class="flex items-center cursor-pointer w-[34%]">
<div class="min-w-[30px] h-[30px] flex items-center justify-center">
<el-image v-if="row.img" class="w-[30px] h-[30px]" :src="img(row.img)" fit="contain">
<template #error>
<div class="image-slot">
<img class="w-[30px] h-[30px]" src="@/addon/shop/assets/category_default.png" />
</div>
</template>
</el-image>
<img v-else class="w-[30px] h-[30px]" src="@/addon/shop/assets/category_default.png" fit="contain" />
</div>
</div>
</div>
<!-- 子级分类 -->
<div v-show="row.child_list && row.isShow">
<div v-for="(item, index) in row.child_list" :key="index" class="flex items-center py-[10px] border-solid border-b-[1px]" :class="{ 'hidden': !row.isShow, 'border-[#e5e7eb]': index == (row.child_list.length - 1) }">
<div class="w-[9%]"></div>
<!-- 子级分类复选框 -->
<div class="w-[7%]">
<el-checkbox v-model="item.threeLevelCheckAll" @change="handleCheckboxChange($event, item, row)" />
</div>
<!-- 子级分类名称 -->
<div class="ml-2 flex flex-col items-start w-[50%]">
<span :title="item.category_name" class="multi-hidden leading-[1.4] mr-5 text-[14px] text-[#666]">
{{ item.category_name }}
</span>
</div>
<!-- 子级分类图片 -->
<div class="flex items-center cursor-pointer w-[34%]">
<div class="min-w-[30px] h-[30px] flex items-center justify-center">
<el-image v-if="row.img" class="w-[30px] h-[30px]" :src="img(row.img)" fit="contain">
<template #error>
<div class="image-slot">
<img class="w-[30px] h-[30px]" src="@/addon/shop/assets/category_default.png" />
</div>
</template>
</el-image>
<img v-else class="w-[30px] h-[30px]" src="@/addon/shop/assets/category_default.png" fit="contain" />
</div>
</div>
</div>
</div>
</div>
<div v-if="!categoryTable.data.length && !categoryTable.loading" class="h-[60px] flex items-center justify-center border-solid border-[#e5e7eb] py-[12px] border-b-[1px]">{{ t('emptyData') }}</div>
</div>
</div>
<div class="flex items-center justify-end mt-[15px]">
<el-button type="primary" @click="saveCategoryId">{{ t('confirm') }}</el-button>
<el-button @click="categoryShowDialog = false">{{ t('cancel') }}</el-button>
</div>
</el-dialog>
</div>
<div class="edit-attr-item-wrap mt-[20px]">
<h3 class="mb-[10px]">{{ t("goodsBuyBtn") }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item :label="t('goodsBtnIsShow')">
<el-switch v-model="diyStore.editComponent.btnStyle.control" />
</el-form-item>
<el-form-item :label="t('goodsCartIncident')" v-if="diyStore.editComponent.btnStyle.control">
<el-radio-group v-model="diyStore.editComponent.btnStyle.cartEvent">
<el-radio label="detail">{{ t('goodsDetail') }}</el-radio>
<el-radio label="cart">{{ t('goodsAddCart') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('goodsBtnStyle')" class="!items-center" v-if="diyStore.editComponent.btnStyle.control">
<div class="flex">
<template v-for="(item,index) in btnStyleList">
<div v-if=" item.isShow == true"
class="cursor-pointer flex items-center justify-center border-[1px] border-solid border-transparent rounded-[6px] py-[5px] px-[8px] mr-[10px]"
:class="{'!border-[var(--el-color-primary)]': diyStore.editComponent.btnStyle.style == item.value}">
<div v-if="item.type == 'icon'"
:class="['nc-iconfont !text-[25px] text-[var(--el-color-primary)]', item.title]" @click="changeBtnStyle(item)"></div>
<div v-if="item.type == 'button'"
class="leading-[1] text-[12px] px-[10px] py-[8px] text-[#fff] rounded-[20px] bg-[var(--el-color-primary)]"
@click="changeBtnStyle(item)">{{ item.title }}</div>
</div>
</template>
</div>
</el-form-item>
<el-form-item :label="t('goodsBtnText')" v-if="diyStore.editComponent.btnStyle.control && diyStore.editComponent.btnStyle.style == 'button'">
<el-input v-model.trim="diyStore.editComponent.btnStyle.text"
:placeholder="t('goodsBtnTextPlaceholder')" clearable maxlength="4" show-word-limit />
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t("goodsShowContent") }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item :label="t('goodsSelectPopupGoodsName')" v-if="diyStore.editComponent.goodsNameStyle.isShow">
<el-switch v-model="diyStore.editComponent.goodsNameStyle.control" />
</el-form-item>
<el-form-item :label="t('goodsPriceColor')" v-if="diyStore.editComponent.priceStyle.isShow">
<el-switch v-model="diyStore.editComponent.priceStyle.control" />
</el-form-item>
<el-form-item :label="t('goodsSaleNum')" v-if="diyStore.editComponent.saleStyle.isShow">
<el-switch v-model="diyStore.editComponent.saleStyle.control" />
</el-form-item>
<el-form-item :label="t('goodsLabel')" v-if="diyStore.editComponent.labelStyle.isShow">
<el-switch v-model="diyStore.editComponent.labelStyle.control" />
</el-form-item>
</el-form>
</div>
</div>
<!-- 样式 -->
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('goodsStyle') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('goodsBgColor')">
<el-color-picker v-model="diyStore.editComponent.elementBgColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('goodsNameColor')">
<el-color-picker v-model="diyStore.editComponent.goodsNameStyle.color" show-alpha :predefine="diyStore.predefineColors" />
<div class="mr-[20px]"></div>
<el-radio-group v-model="diyStore.editComponent.goodsNameStyle.fontWeight">
<el-radio :label="'normal'">{{ t('fontWeightNormal') }}</el-radio>
<el-radio :label="'bold'">{{ t('fontWeightBold') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('imageRounded')">
<el-slider v-model="diyStore.editComponent.imgElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
<el-form-item :label="t('goodsPriceColor')">
<el-color-picker v-model="diyStore.editComponent.priceStyle.color" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('goodsSaleColor')">
<el-color-picker v-model="diyStore.editComponent.saleStyle.color" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('topRounded')">
<el-slider v-model="diyStore.editComponent.topElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
<el-form-item :label="t('bottomRounded')">
<el-slider v-model="diyStore.editComponent.bottomElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap" v-if="diyStore.editComponent.btnStyle.control">
<h3 class="mb-[10px]">{{ t("goodsBuyBtn") }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('goodsTextColor')">
<el-color-picker v-model="diyStore.editComponent.btnStyle.textColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('listFrameColor')">
<el-color-picker v-model="diyStore.editComponent.btnStyle.startBgColor" show-alpha :predefine="diyStore.predefineColors" />
<icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]" />
<el-color-picker v-model="diyStore.editComponent.btnStyle.endBgColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
</el-form>
</div>
<!-- 组件样式 -->
<slot name="style"></slot>
</div>
</template>
<script lang="ts" setup>
import { t } from '@/lang'
import { img } from '@/utils/common'
import useDiyStore from '@/stores/modules/diy'
import { ref, reactive, onMounted, nextTick } from 'vue'
import { ElTable } from 'element-plus'
import { range } from 'lodash-es'
import Sortable from 'sortablejs'
import { getCategoryTree, getCategoryList } from '@/addon/shop/api/goods'
import goodsSelectPopup from '@/addon/shop/views/goods/components/goods-select-popup.vue'
const diyStore: any = useDiyStore()
diyStore.editComponent.ignore = ['componentBgUrl'] //
//
diyStore.editComponent.verify = (index: number) => {
const res = { code: true, message: '' }
if (diyStore.value[index].source == 'custom') {
diyStore.value[index].list.forEach((item: any) => {
if (item.source === 'category') {
if (item.goods_category == '') {
res.code = false
res.message = t('goodsCategoryPlaceholder')
return res
}
} else if (item.source == 'custom') {
if (item.goods_ids.length == 0) {
res.code = false
res.message = t('goodsPlaceholder')
}
}
});
} else if (diyStore.value[index].source == 'goods_category') {
if (diyStore.value[index].goods_category == '') {
res.code = false
res.message = t('goodsCategoryPlaceholder')
return res
}
}
return res
}
diyStore.editComponent.list.forEach((item: any) => {
if (!item.id) item.id = diyStore.generateRandom()
})
//
const firstCategoryShowDialog = ref(false)
const firstCategoryTable = reactive({
loading: true,
data: [],
searchParam: {
level: 1
}
})
const firstCategoryTableRef = ref<InstanceType<typeof ElTable>>()
/**
* 获取商品分类列表
*/
const loadCategoryList = () => {
firstCategoryTable.loading = true
getCategoryList({
...firstCategoryTable.searchParam
}).then(res => {
firstCategoryTable.loading = false
firstCategoryTable.data = res.data
}).catch(() => {
firstCategoryTable.loading = false
})
}
const saveFirstCategoryId = () => {
diyStore.editComponent.goods_category = currFirstCategory.category_id
diyStore.editComponent.goods_category_name = currFirstCategory.category_name;
firstCategoryShowDialog.value = false
}
const firstCategoryShowDialogOpen = () => {
firstCategoryShowDialog.value = true
if (currFirstCategory) {
setTimeout(() => {
firstCategoryTableRef.value!.setCurrentRow(currFirstCategory)
}, 200)
}
}
const btnStyleList = reactive([
{
isShow: true,
type: 'button',
title: diyStore.editComponent.btnStyle.text,
value: 'button'
},
{
isShow: true,
type: 'icon',
title: 'nc-icon-jiahaoV6xx',
value: 'nc-icon-jiahaoV6xx'
},
{
isShow: true,
type: 'icon',
title: 'nc-icon-gouwuche1',
value: 'nc-icon-gouwuche1'
}
])
diyStore.editComponent.btnStyle.style = 'nc-icon-jiahaoV6xx';
const changeBtnStyle = (item: any) => {
diyStore.editComponent.btnStyle.style = item.value
}
const clearCategory = () => {
diyStore.editComponent.goods_category = ''
diyStore.editComponent.goods_category_name = '';
}
//
let currFirstCategory: any = {}
const handleCurrentCategoryChange = (val: string | any[]) => {
currFirstCategory = val
}
//
const categoryShowDialog = ref(false)
const goodsBoxRef = ref()
const categoryTable = reactive({
loading: true,
data: []
})
onMounted(() => {
loadCategoryTree()
loadCategoryList()
nextTick(() => {
const sortable = Sortable.create(goodsBoxRef.value, {
group: 'item-wrap',
animation: 200,
onEnd: event => {
const temp = diyStore.editComponent.list[event.oldIndex!]
diyStore.editComponent.list.splice(event.oldIndex!, 1)
diyStore.editComponent.list.splice(event.newIndex!, 0, temp)
sortable.sort(
range(diyStore.editComponent.list.length).map(value => {
return value.toString()
})
)
}
})
})
window.addEventListener("resize", getScrollBarWidth);
})
/**
* 获取商品分类列表
*/
// let currCategoryData: any = null
const loadCategoryTree = () => {
categoryTable.loading = true
getCategoryTree().then(res => {
categoryTable.loading = false
categoryTable.data = res.data
categoryTable.data.forEach((item: any) => {
//
item.isShow = false; //
item.isSecondLevelIndeterminate = false; //
item.secondLevelCheckAll = false; //
// child_list
if (item.child_list && item.child_list.length) {
item.child_list.forEach((childItem: any) => {
childItem.threeLevelCheckAll = false //
})
}
})
}).catch(() => {
categoryTable.loading = false
})
}
const addItem = () => {
diyStore.editComponent.list.push({
id: diyStore.generateRandom(),
title: "分类",
desc: "分类描述",
source: "all",
goods_category: '',
goods_category_name: '请选择',
goods_ids: [],
imageUrl: ''
})
}
const scrollBarWidth = ref(0);
const tableBodyRef = ref(null);
const getScrollBarWidth = () => {
nextTick(() => {
if (tableBodyRef.value) {
scrollBarWidth.value = tableBodyRef.value.offsetWidth - tableBodyRef.value.clientWidth;
}
})
}
//
let selectIndex = 0; //
const selectedCategories = ref({});
// /
const secondLevelArrowChange = (row:any) => {
row.isShow = !row.isShow;
nextTick(() => getScrollBarWidth());
};
const saveCategoryId = () => {
const selected = selectedCategories.value[selectIndex];
if (!selected || !selected.category_id) { // `category_id`
ElMessage({
type: 'warning',
message: t('请选择分类'),
});
return;
}
diyStore.editComponent.list[selectIndex].goods_category = selectedCategories.value[selectIndex].category_id
diyStore.editComponent.list[selectIndex].goods_category_name = selectedCategories.value[selectIndex].category_name;
categoryShowDialog.value = false
}
const clearAllSelections = () => {
categoryTable.data.forEach((row: any) => {
row.secondLevelCheckAll = false;
if (row.child_list) {
row.child_list.forEach((child: any) => {
child.threeLevelCheckAll = false;
});
}
});
}
const categoryShowDialogOpen = (index:any) => {
selectIndex = index;
clearAllSelections();
// isShow
categoryTable.data.forEach((row: any) => {
row.isShow = false; //
});
// `selectedCategories.value[selectIndex]`
if (!selectedCategories.value[selectIndex]) {
selectedCategories.value[selectIndex] = {}; //
}
//
nextTick(() => {
const selectedCategory = diyStore.editComponent.list[selectIndex];
// selectedCategories.value[selectIndex]
if (!selectedCategories.value[selectIndex]) {
selectedCategories.value[selectIndex] = {}; //
selectedCategories.value[selectIndex].category_id = selectedCategory.goods_category;
selectedCategories.value[selectIndex].category_name = selectedCategory.goods_category_name;
}
if (selectedCategory) {
categoryTable.data.forEach((row: any) => {
if (row.category_id === selectedCategory.goods_category) {
row.secondLevelCheckAll = true;
row.isShow = true; //
}
if (row.child_list) {
row.child_list.forEach((child: any) => {
if (child.category_id === selectedCategory.goods_category) {
child.threeLevelCheckAll = true;
row.isShow = true;
}
});
}
});
}
});
nextTick(() => getScrollBarWidth());
categoryShowDialog.value = true;
}
//
const handleCheckboxChange = (checked: any, target: any, parentRow: any) => {
clearAllSelections(); //
if (checked) {
//
if (parentRow) {
//
target.threeLevelCheckAll = checked;
selectedCategories.value[selectIndex] = target;
parentRow.isShow = true; //
} else {
//
target.secondLevelCheckAll = checked;
selectedCategories.value[selectIndex] = target;
target.isShow = true; //
}
} else {
// ID
delete selectedCategories.value[selectIndex];
}
}
defineExpose({})
</script>
<style lang="scss" scoped>
.arrow-show {
transform: rotate(90deg) !important; /* 提高优先级 */
}
</style>

View File

@ -0,0 +1,142 @@
<template>
<div>
<!-- 内容 -->
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
<!-- <div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('selectStyle') }}</h3>
<div class="flex items-center mb-[18px] rounded overflow-hidden">
<span
class="iconfont icongudingzhanshi border-[1px] border-solid border-[#eee] cursor-pointer flex-1 flex items-center justify-center py-[5px]"
:class="{ 'border-[var(--el-color-primary)] text-[var(--el-color-primary)]': diyStore.editComponent.style == 'style-1' }"
@click="diyStore.editComponent.style = 'style-1'"></span>
<span
class="iconfont icontuwendaohang3 border-[1px] border-solid border-[#eee] cursor-pointer flex-1 flex items-center justify-center py-[5px]"
:class="{ 'border-[var(--el-color-primary)] text-[var(--el-color-primary)]': diyStore.editComponent.style == 'style-2' }"
@click="diyStore.editComponent.style = 'style-2'"></span>
<span
class="iconfont iconshangpinliebiaohengxianghuadong border-[1px] border-solid border-[#eee] cursor-pointer flex-1 flex items-center justify-center py-[5px]"
:class="{ 'border-[var(--el-color-primary)] text-[var(--el-color-primary)]': diyStore.editComponent.style == 'style-3' }"
@click="diyStore.editComponent.style = 'style-3'"></span>
</div>
</div> -->
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t("selectSource") }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('sortWay')">
<el-radio-group v-model="diyStore.editComponent.sortWay">
<el-radio label="total_order_num">{{ t('default') }}</el-radio>
<el-radio label="total_exchange_num">{{ t('sales') }}</el-radio>
<el-radio label="price">{{ t('price') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('goodsSelectPopupSelectGoodsButton')">
<el-radio-group v-model="diyStore.editComponent.source" :title="t('goodsSelectPopupSelectGoodsButton')">
<el-radio label="all">{{ t('goodsSelectPopupAllGoods') }}</el-radio>
<el-radio label="custom">{{ t('manualSelectionSources') }}</el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item :label="t('selectCategory')" v-if="diyStore.editComponent.source == 'category'">
<div class="flex items-center w-full">
<div class="cursor-pointer ml-auto" @click="categoryShowDialogOpen">
<span class="text-[var(--el-color-primary)]">{{ diyStore.editComponent.goods_category_name }}</span>
<span class="iconfont iconxiangyoujiantou"></span>
</div>
</div>
</el-form-item>
<el-form-item :label="t('goodsNum')" v-if="diyStore.editComponent.source == 'all' || diyStore.editComponent.source == 'category'">
<div class="flex items-center w-full ml-[5px]">
<el-slider class="flex-1" v-model="diyStore.editComponent.num" :min="1" max="20" size="small" />
<span class="ml-[15px]">{{ diyStore.editComponent.num }}</span>
</div>
</el-form-item> -->
<el-form-item :label="t('customGoods')" v-if="diyStore.editComponent.source == 'custom'">
<el-button type="primary" @click="goodsSelectPopupRef.show(diyStore.editComponent.goods_ids)">
{{ t('goodsSelectPopupSelectGoodsButton') }}
</el-button>
<div class="inline-block ml-[10px] text-[14px]"
v-show="diyStore.editComponent.goods_ids.length">
<span>{{ t('goodsSelectPopupSelect') }}</span>
<span class="text-primary mx-[2px]">{{ diyStore.editComponent.goods_ids.length }}</span>
<span>{{ t('goodsSelectPopupPiece') }}</span>
</div>
<goods-select-popup ref="goodsSelectPopupRef" :min="1" @select="goodsSelect" />
</el-form-item>
</el-form>
</div>
</div>
<!-- 样式 -->
<div class="style-wrap" v-if="diyStore.editTab == 'style'">
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('goodsStyle') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('goodsNameColor')">
<el-color-picker v-model="diyStore.editComponent.goodsNameStyle.color" show-alpha :predefine="diyStore.predefineColors" />
<div class="mr-[20px]"></div>
<el-radio-group v-model="diyStore.editComponent.goodsNameStyle.fontWeight">
<el-radio :label="'normal'">{{ t('fontWeightNormal') }}</el-radio>
<el-radio :label="'bold'">{{ t('fontWeightBold') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('imageRounded')">
<el-slider v-model="diyStore.editComponent.imgElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
<el-form-item :label="t('goodsNumColor')">
<el-color-picker v-model="diyStore.editComponent.saleStyle.color" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('goodsPriceColor')">
<el-color-picker v-model="diyStore.editComponent.priceStyle.mainColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('topRounded')">
<el-slider v-model="diyStore.editComponent.topElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
<el-form-item :label="t('bottomRounded')">
<el-slider v-model="diyStore.editComponent.bottomElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
</el-form>
</div>
<!-- 组件样式 -->
<slot name="style"></slot>
</div>
</div>
</template>
<script lang="ts" setup>
import { t } from '@/lang'
import useDiyStore from '@/stores/modules/diy'
import { ref, reactive } from 'vue'
import goodsSelectPopup from '@/addon/shop/views/marketing/exchange/components/goods-select-popup.vue'
const diyStore: any = useDiyStore()
diyStore.editComponent.ignore = [] //
//
diyStore.editComponent.verify = (index: number) => {
const res = { code: true, message: '' }
if (diyStore.value[index].source == 'category') {
if (diyStore.value[index].goods_category == '') {
res.code = false
res.message = t('goodsCategoryPlaceholder')
}
} else if (diyStore.value[index].source == 'custom') {
if (diyStore.value[index].goods_ids.length == 0) {
res.code = false
res.message = t('goodsPlaceholder')
}
}
return res
}
const goodsSelectPopupRef = ref()
const goodsSelect = (val: any) => {
diyStore.editComponent.goods_ids = val.map((el: any) => el.id)
}
defineExpose({})
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,38 @@
<template>
<!-- 内容 -->
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('memberStyle') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('bgUrl')">
<upload-image v-model="diyStore.editComponent.bgUrl" :limit="1" />
</el-form-item>
</el-form>
</div>
</div>
<!-- 样式 -->
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
<!-- 组件样式 -->
<slot name="style"></slot>
</div>
</template>
<script lang="ts" setup>
import { t } from '@/lang'
import useDiyStore from '@/stores/modules/diy'
const diyStore: any = useDiyStore()
diyStore.editComponent.ignore = ['componentBgColor', 'componentBgUrl'] //
//
diyStore.editComponent.verify = (index: number) => {
const res = { code: true, message: '' }
return res
}
defineExpose({})
</script>
<style lang="scss" scoped></style>

Some files were not shown because too many files have changed in this diff Show More