# Conflicts:
#	src/pages/tabbar/integrated.vue
This commit is contained in:
zjf committed 2025-09-16 18:22:34 +08:00
commit 91e77c8232
18 files changed
+269 -330

No files matched your search

+1 -1
View File
@@ -107,7 +107,7 @@ const tabbar: TabbarItem[] = [
iconPath: "/static/tabbar/integrated.png",
selectedIconPath: "/static/tabbar/integrated-select.png",
launchParam: ["integrated"],
auth: [],
auth: ["user"],
},
{
pagePath: "pages/tabbar/my",
+1 -1
View File
@@ -174,7 +174,7 @@
}
},
{
"path": "goods-categary",
"path": "categary-manage",
"style": {
"navigationBarTitleText": "商品分类管理",
"disableScroll": true
+5 -4
View File
@@ -79,21 +79,22 @@ const doSubmit = async () => {
<template>
<crud-layout :title="state.detailId ? '修改人脸' : '人脸上传'" bg="/static/page/blue-bg.png">
<view class="h-30rpx" />
<view class="card p-x-30rpx p-y-40rpx">
<view class="card px-30rpx py-40rpx">
<tm-form ref="applyFormRef" v-model="applyForm" :rules="applyFormRules" direction="vertical">
<tm-form-item name="faceImg" label="人脸图片" :required="false" :showBottom="false">
<view class="h-10rpx" />
<tm-upload-photo
:maxFileSize="1024 * 1024"
:maxFileSize="1024 * 1024 * 5"
:header="state.header"
:rangUrl="applyForm.faceImg[0]"
:beforeComplete="beforeComplete"
v-model="applyForm.faceImg"
:maxCount="1"
:column="2"
img-height="320"
:url="'/api/admin/sys-file/upload'"
/>
</tm-form-item>
<view class="sub-text-20 c-red!">注:图片大小不超过1M</view>
<view class="sub-text-20 c-red!">注:图片大小不超过5M</view>
</tm-form>
</view>
<view
+15 -18
View File
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { ref, reactive } from "vue"
import { onLoad, onShow } from "@dcloudio/uni-app"
import { onShow } from "@dcloudio/uni-app"
import { hooks, router } from "@@/index"
import { faceApi } from "@@/api"
@@ -19,7 +19,6 @@ const query = (page: number, pagesize: number) => {
pageHook.startPage(page, pagesize, false, state.pa)
}
onLoad(() => {})
onShow(() => {
if (pageRef.value) {
pageRef.value.reload(false)
@@ -64,27 +63,25 @@ const doDel = (id: string) => {
@scroll="pageHook.scroll"
>
<view class="h-20rpx" />
<view class="flex-col card p-x-20rpx p-y-30rpx m-b-20rpx" v-for="(v, i) in pageHook.list" :key="v.id">
<view class="flex items-center justify-between second-text-26">
<div class="flex items-center">
<image mode="aspectFill" class="w-100rpx h-100rpx rounded-50rpx" :src="filePath(v.faceImg)" />
<view class="w-30rpx" />
<text class="font-600">{{ v.username }}</text>
</div>
<div class="flex items-center">
<!-- <text class="botder_btn" @click="doEdit(v.id)">修改</text> -->
<view class="w-30rpx" />
<tm-modal v-if="false" title="确定删除吗?" height="auto" @confirm="doDel(v.id)">
<template v-slot:trigger><text class="botder_btn">删除</text></template>
</tm-modal>
</div>
<view class="card flex-col-center p-20rpx mb-20rpx" v-for="(v, i) in pageHook.list" :key="v.id">
<image
class="w-300rpx h-300rpx rounded-50rpx mb-2"
:src="filePath(v.faceImg)"
mode="aspectFill"
@click.stop="hooks.usePreviewImage(filePath(v.faceImg))"
/>
<view class="sub-text-30 mb-2">{{ v.username }}·{{ v.deptName }}</view>
<view v-if="false" class="flex w-full items-center justify-end gap-2 second-text-26">
<text class="botder_btn" @click="doEdit(v.id)">修改</text>
<tm-modal title="确定删除吗?" height="auto" @confirm="doDel(v.id)">
<template v-slot:trigger><text class="botder_btn">删除</text></template>
</tm-modal>
</view>
</view>
<template #bottom>
<view v-if="false" class="bg-#0094ff p-y-20rpx text-center rounded-40rpx m-x-30rpx m-t-60rpx sub-text-30 color-white!" @tap="doAdd">
<view v-if="false" class="bg-#0094ff py-20rpx text-center rounded-40rpx mx-30rpx my-4 sub-text-30 color-white!" @tap="doAdd">
添加
</view>
<view class="h-30rpx" />
</template>
</z-paging>
</crud-layout>
+24 -24
View File
@@ -7,60 +7,60 @@
</tm-alert>
<tm-sheet :margin="[0, 20]" class="second-text-30">
<tm-text class="prefix_title" font-size="32">访客信息</tm-text>
<view class="flex justify-between mt-1">
<text>访客姓名:</text>
<view class="flex mt-1">
<text class="w-220rpx mr-6">访客姓名</text>
<text>{{ detail.name }}</text>
</view>
<view class="flex justify-between mt-1">
<text>访客手机号:</text>
<view class="flex mt-1">
<text class="w-220rpx mr-6">访客手机号</text>
<text>{{ detail.mobile }}</text>
</view>
<view class="flex justify-between mt-1">
<text>来访公司名称:</text>
<view class="flex mt-1">
<text class="w-220rpx mr-6">来访公司名称</text>
<text>{{ detail.companyName }}</text>
</view>
<view class="flex justify-between mt-1">
<text>来访原因:</text>
<view class="flex mt-1">
<text class="w-220rpx mr-6">来访原因</text>
<text>{{ visitorReasonList.find((item) => item.id == detail.reasonValue)?.title || "" }}</text>
</view>
<view class="flex justify-between mt-1">
<text>接待人:</text>
<view class="flex mt-1">
<text class="w-220rpx mr-6">接待人</text>
<text>{{ detail.userName }}</text>
</view>
<view class="flex justify-between mt-1">
<text>来访人数:</text>
<view class="flex mt-1">
<text class="w-220rpx mr-6">来访人数</text>
<text>{{ detail.peopleNumber }}</text>
</view>
<view class="flex justify-between mt-1">
<text>是否可随行人员:</text>
<view class="flex mt-1">
<text class="w-220rpx mr-6">是否可随行人员</text>
<text>{{ detail.isOtherPeople == "1" ? "是" : "否" }}</text>
</view>
<view class="flex justify-between mt-1">
<text>是否可驾驶车辆:</text>
<view class="flex mt-1">
<text class="w-220rpx mr-6">是否可驾驶车辆</text>
<text>{{ detail.isDrive == "1" ? "是" : "否" }}</text>
</view>
</tm-sheet>
<tm-sheet :margin="[0, 20]" class="second-text-30">
<tm-text class="prefix_title" font-size="32">时间</tm-text>
<view class="flex justify-between mt-1">
<text>预计来访时间:</text>
<view class="flex mt-1">
<text class="w-220rpx mr-6">预计来访时间</text>
<text>{{ detail.vendDate }}</text>
</view>
<view class="flex justify-between mt-1">
<text>预计离开时间:</text>
<view class="flex mt-1">
<text class="w-220rpx mr-6">预计离开时间</text>
<text>{{ detail.vstartDate }}</text>
</view>
</tm-sheet>
<tm-sheet :margin="[0, 20]" class="second-text-30">
<tm-text class="prefix_title" font-size="32">其他</tm-text>
<view class="flex justify-between mt-1">
<text>创建方式:</text>
<view class="flex mt-1">
<text class="w-220rpx mr-6">创建方式</text>
<text>{{ detail.type == "1" ? "主动申请" : detail.type == "2" ? "邀约" : "后台创建" }}</text>
</view>
<view class="flex justify-between mt-1">
<text>提交时间:</text>
<view class="flex mt-1">
<text class="w-220rpx mr-6">提交时间</text>
<text>{{ detail.createTime }}</text>
</view>
</tm-sheet>
+2 -3
View File
@@ -1,8 +1,7 @@
<template>
<crud-layout mainIsScroll bg="/static/page/blue-bg.png">
<view class="h-20rpx" />
<tm-sheet margin="[0]">
<view class="mx-auto mb-1 text-center text-lg">访客邀约</view>
<tm-sheet :margin="[0]">
<tm-alert :border="['2']" :borderColor="['#89c0ff']" :darkBorderColor="['#3b5066']">访客邀约请填写以下信息</tm-alert>
<tm-form @submit="onsubmit" v-model="form" :rules="rules" direction="vertical">
<!-- <tm-form-item name="name" label="访客姓名">
@@ -70,7 +69,7 @@
<tm-form-item name="isDrive" label="是否可驾驶车辆" direction="horizontal" labelWidth="500">
<tm-switch v-model="form.isDrive" />
</tm-form-item>
<tm-button form-type="submit" block>提交</tm-button>
<tm-button class="mt-4" form-type="submit" block>提交</tm-button>
</tm-form>
</tm-sheet>
@@ -2,19 +2,22 @@
<crud-layout mainIsScroll bg="/static/page/blue-bg.png">
<view class="h-40rpx"></view>
<view class="card flex justify-between items-center mb-2 mx-20rpx p-4" v-for="item in categoryList" :key="item.id">
<view class="flex-row-center gap-2">
<text class="sub-text-30">{{ item.name }}</text>
<text class="c-#0094ff! sub-text-24 mr-20rpx px-2 bg-#0094ff22 rounded-2">{{ item.status == "0" ? "正常" : "禁用" }}</text>
</view>
<view class="flex-row-center gap-2">
<tm-button class="px-2" size="xs" :linear="['to right', '#62abff', '#0779FD99']" @click="editCategary(item)">编辑</tm-button>
<tm-button class="px-2" size="xs" :linear="['to right', '#FFC328', '#E2000099']" @click="delCategary(item.id)">删除</tm-button>
<image :src="filePath(item.picUrl)" class="size-100rpx mr-4" />
<view class="flex-1 flex-col">
<view class="flex justify-between items-center">
<text class="sub-text-30">{{ item.name }}</text>
<text class="c-#0094ff! sub-text-24 px-2 bg-#0094ff22 rounded-2">{{ item.status == "0" ? "正常" : "禁用" }}</text>
</view>
<view class="flex justify-end items-center gap-2 mt-2">
<tm-button class="px-2" size="s" :linear="['to right', '#62abff', '#0779FD99']" @click="editCategary(item)">编辑</tm-button>
<tm-button class="px-2" size="s" :linear="['to right', '#FFC328', '#E2000099']" @click="delCategary(item.id)">删除</tm-button>
</view>
</view>
</view>
<view class="h-100rpx"></view>
<view class="fixed bottom-80rpx left-40rpx">
<tm-button class="px-2" size="s" :linear="['to right', '#0779FD', '#62abff']" @click="addCategary">新增分类</tm-button>
<tm-button class="px-2" size="m" :linear="['to right', '#0779FD', '#62abff']" @click="addCategary">新增分类</tm-button>
</view>
<tm-drawer
@@ -47,9 +50,20 @@
</tm-radio-group>
</tm-form-item>
<view class="prefix_title mb-2">父级分类</view>
<view class="card my-2">
<shop-categary-tree v-model="selectedNode" :node-data="categoryTree" />
<view class="my-2">
<node-tree v-model="selectedNode" :node-data="categoryTree" />
</view>
<view class="prefix_title mb-2">分类图</view>
<tm-upload-photo
v-model="form.picUrl"
:maxFileSize="1024 * 1024 * 5"
:url="'/api/admin/sys-file/upload'"
:header="hooks.useDefaultHeader()"
:column="1.6"
img-height="360"
:max-count="1"
/>
<view class="sub-text-20 c-red! mb-2">注:图片大小不超过5M</view>
<tm-button form-type="submit" block>保 存</tm-button>
</tm-form>
</view>
@@ -61,29 +75,28 @@
import { prosperityShopApi } from "@/sheep/api"
import { onLoad } from "@dcloudio/uni-app"
import { reactive, ref } from "vue"
import ShopCategaryTree from "@/sheep/components/shop-categary-tree.vue"
import NodeTree from "@/sheep/components/node-tree.vue"
import { useCategaryManage } from "./use-categary-manage"
import { hooks } from "@@/index"
import { filePath } from "@/sheep/utils/file"
const categoryList = ref<any>([])
const categaryFormDrawer = ref(false)
const form = reactive({
const form = reactive<any>({
id: "",
name: "",
status: "0", // 状态(0正常,1下架)
parentId: "",
picUrl: [],
})
const categoryTree = ref([])
const selectedNode = ref<any>({})
const { categoryList, getGoodsCategory, delCategary } = useCategaryManage()
onLoad(() => {
getGoodsCategory()
})
const getGoodsCategory = async () => {
const { code, data, msg } = await prosperityShopApi.getGoodsCategory()
if (code != 0) return uni.showToast({ title: msg || "商品分类获取失败", icon: "none" })
categoryList.value = data
}
const getCategaryTree = async () => {
const { code, data, msg } = await prosperityShopApi.getGoodsCategoryTree()
if (code != 0) return uni.showToast({ title: msg || "商品分类获取失败", icon: "none" })
@@ -98,6 +111,7 @@ const addCategary = () => {
name: "",
status: "0",
parentId: "",
picUrl: [],
})
selectedNode.value = {}
categaryFormDrawer.value = true
@@ -106,7 +120,8 @@ const addCategary = () => {
// 编辑分类
const editCategary = (item) => {
getCategaryTree()
Object.assign(form, item)
const picUrl = [{ path: filePath(item.picUrl), response: JSON.stringify({ data: { url: item.picUrl } }) }]
Object.assign(form, { ...item, picUrl })
selectedNode.value = { id: item.parentId }
categaryFormDrawer.value = true
}
@@ -114,9 +129,13 @@ const editCategary = (item) => {
// 提交保存
const saveCategary = async () => {
if (!form.name) return uni.showToast({ title: "请填写分类名称", icon: "none" })
if (selectedNode.value.id === form.id) return uni.showToast({ title: "父级分类不能是自身", icon: "none" })
if (selectedNode.value.id === form.id) return uni.showToast({ title: "父级分类不能是自己", icon: "none" })
try {
const params = { ...form, parentId: selectedNode.value.id }
const params = {
...form,
parentId: selectedNode.value.id,
picUrl: JSON.parse(form.picUrl[0].response).data.url,
}
uni.showLoading({ title: "保存中..." })
const { code, msg } = await (form.id ? prosperityShopApi.editCategary(params) : prosperityShopApi.addCategary(params))
if (code != 0) return uni.showToast({ title: msg || "保存失败", icon: "none" })
@@ -127,23 +146,4 @@ const saveCategary = async () => {
uni.hideLoading()
}
}
// 删除分类
const delCategary = async (id: string) => {
if (!id) return uni.showToast({ title: "分类id不存在", icon: "none" })
uni.showModal({
title: "确定删除分类吗?",
content: "",
success: async (res) => {
if (res.confirm) {
uni.showLoading({ title: "正在删除" })
const { code, msg } = await prosperityShopApi.delCategary(id)
if (code != 0) return uni.showToast({ title: msg || "删除失败", icon: "none" })
uni.showToast({ title: "删除成功", icon: "none" })
getGoodsCategory()
uni.hideLoading()
}
},
})
}
</script>
+5 -129
View File
@@ -74,7 +74,8 @@
</tm-form-item>
<view class="sub-text-20 c-red! mb-2">注:图片大小不超过5M</view>
<tm-form-item name="description" label="详情描述" direction="vertical" required="">
<rich-text-input v-model="form.description" placeholder="点击编辑详情描述..." label="详情描述" />
<!-- <rich-text-input v-model="form.description" placeholder="点击编辑详情描述..." label="详情描述" /> -->
<tm-input v-model="form.description" type="textarea" :showClear="false" placeholder="请输入详情描述"></tm-input>
</tm-form-item>
<tm-form-item name="priceType" label="定价方式">
<tm-radio-group v-model="form.priceType" align="flex-end">
@@ -151,7 +152,7 @@
title="请选择分类"
:showClose="true"
:showFooter="false"
size="auto"
size="70%"
v-model:show="showCategaryPicker"
:duration="100"
zIndex="100"
@@ -168,7 +169,6 @@ import { reactive, ref } from "vue"
import { hooks } from "@@/index"
import { prosperityShopApi } from "@/sheep/api"
import ShopCategaryPicker from "@/sheep/components/shop-categary-picker.vue"
import RichTextInput from "@/sheep/components/rich-text-input/rich-text-input.vue"
import { onLoad } from "@dcloudio/uni-app"
import { filePath } from "@/sheep/utils/file"
import { displayPrice, storePrice } from "@/sheep/utils/formatMoney"
@@ -228,7 +228,7 @@ const getGoodsDetail = async (id: string) => {
uni.showLoading({ title: "加载中..." })
const { code, data, msg } = await prosperityShopApi.getGoodsDetail(id)
if (code != 0 || !data) return uni.showToast({ title: msg || "商品详情获取失败", icon: "none" })
const skuList = data.skuList.map((item: any) => ({ ...item, status: item.status.toString() }))
const skuList = data.skuList.map((item: any) => ({ ...item, status: item.status.toString(), price: displayPrice(item.price, "") }))
const coverImage = [{ path: filePath(data.coverImage), response: JSON.stringify({ data: { url: data.coverImage } }) }]
const categoryName = categoryList.value.find((item: any) => item.id == data.categoryId)?.name
const saleStartTime = data.saleStartTime
@@ -236,11 +236,6 @@ const getGoodsDetail = async (id: string) => {
_saleStartTimeStr.value = saleStartTime
_saleEndTimeStr.value = saleEndTime
// 转换HTML为Markdown格式
const description = convertHtmlToMarkdown(data.description || "")
console.log("原始HTML:", data.description)
console.log("转换后Markdown:", description)
Object.assign(form, {
...data,
status: data.status.toString(),
@@ -251,95 +246,11 @@ const getGoodsDetail = async (id: string) => {
categoryName,
saleStartTime,
saleEndTime,
skuList: data.skuList.map((item: any) => {
return {
...item,
price: displayPrice(item.price, ""),
}
}),
description,
skuList,
})
uni.hideLoading()
}
// HTML转Markdown函数
const convertHtmlToMarkdown = (html: string): string => {
if (!html) return ""
// 移除HTML标签,保留文本内容
let markdown = html
// 处理标题
.replace(/<h1[^>]*>(.*?)<\/h1>/gi, "# $1\n")
.replace(/<h2[^>]*>(.*?)<\/h2>/gi, "## $1\n")
.replace(/<h3[^>]*>(.*?)<\/h3>/gi, "### $1\n")
.replace(/<h4[^>]*>(.*?)<\/h4>/gi, "#### $1\n")
.replace(/<h5[^>]*>(.*?)<\/h5>/gi, "##### $1\n")
.replace(/<h6[^>]*>(.*?)<\/h6>/gi, "###### $1\n")
// 处理段落
.replace(/<p[^>]*>(.*?)<\/p>/gi, "$1\n\n")
// 处理换行
.replace(/<br\s*\/?>/gi, "\n")
// 处理粗体
.replace(/<strong[^>]*>(.*?)<\/strong>/gi, "**$1**")
.replace(/<b[^>]*>(.*?)<\/b>/gi, "**$1**")
// 处理斜体
.replace(/<em[^>]*>(.*?)<\/em>/gi, "*$1*")
.replace(/<i[^>]*>(.*?)<\/i>/gi, "*$1*")
// 处理下划线
.replace(/<u[^>]*>(.*?)<\/u>/gi, "__$1__")
// 处理链接
.replace(/<a[^>]*href=["']([^"']*)["'][^>]*>(.*?)<\/a>/gi, "[$2]($1)")
// 处理图片 - 更全面的图片处理
.replace(/<img[^>]*src=["']([^"']*)["'][^>]*alt=["']([^"']*)["'][^>]*>/gi, (match, src, alt) => {
// 直接使用原始URL,不添加额外前缀
console.log("HTML图片转换 - 原始:", match)
console.log("HTML图片转换 - src:", src)
console.log("HTML图片转换 - alt:", alt)
return `![${alt || "图片"}](${src})`
})
.replace(/<img[^>]*src=["']([^"']*)["'][^>]*>/gi, (match, src) => {
// 直接使用原始URL,不添加额外前缀
console.log("HTML图片转换 - 原始:", match)
console.log("HTML图片转换 - src:", src)
return `![图片](${src})`
})
// 处理列表
.replace(/<ul[^>]*>(.*?)<\/ul>/gis, (match, content) => {
return content.replace(/<li[^>]*>(.*?)<\/li>/gi, "- $1\n")
})
.replace(/<ol[^>]*>(.*?)<\/ol>/gis, (match, content) => {
let index = 1
return content.replace(/<li[^>]*>(.*?)<\/li>/gi, () => `${index++}. $1\n`)
})
// 处理引用
.replace(/<blockquote[^>]*>(.*?)<\/blockquote>/gis, (match, content) => {
return content.replace(/\n/g, "\n> ") + "\n"
})
// 处理代码
.replace(/<code[^>]*>(.*?)<\/code>/gi, "`$1`")
.replace(/<pre[^>]*>(.*?)<\/pre>/gis, "```\n$1\n```")
// 处理分割线
.replace(/<hr[^>]*>/gi, "---\n")
// 处理表格(简单处理)
.replace(/<table[^>]*>(.*?)<\/table>/gis, (match, content) => {
// 这里可以添加更复杂的表格处理逻辑
return content.replace(/<tr[^>]*>(.*?)<\/tr>/gi, (trMatch, trContent) => {
const cells = trContent.match(/<t[hd][^>]*>(.*?)<\/t[hd]>/gi)
if (cells) {
return "| " + cells.map((cell) => cell.replace(/<[^>]*>/g, "")).join(" | ") + " |\n"
}
return ""
})
})
// 移除剩余的HTML标签
.replace(/<[^>]*>/g, "")
// 清理多余的换行
.replace(/\n{3,}/g, "\n\n")
.trim()
return markdown
}
const getGoodsCategory = async () => {
const { code, data, msg } = await prosperityShopApi.getGoodsCategory()
if (code != 0) return uni.showToast({ title: msg || "商品分类获取失败", icon: "none" })
@@ -381,46 +292,12 @@ const addSkuListItem = () => {
})
}
// Markdown转HTML函数
const convertMarkdownToHtml = (markdown: string): string => {
if (!markdown) return ""
let html = markdown
// 处理标题
.replace(/^# (.*$)/gim, "<h1>$1</h1>")
.replace(/^## (.*$)/gim, "<h2>$1</h2>")
.replace(/^### (.*$)/gim, "<h3>$1</h3>")
.replace(/^#### (.*$)/gim, "<h4>$1</h4>")
.replace(/^##### (.*$)/gim, "<h5>$1</h5>")
.replace(/^###### (.*$)/gim, "<h6>$1</h6>")
// 处理粗体
.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>")
// 处理斜体
.replace(/\*(.*?)\*/g, "<em>$1</em>")
// 处理链接
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>')
// 处理图片
.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '<img src="$2" alt="$1" style="max-width: 100%; height: auto;" />')
// 处理段落
.replace(/^(?!<[h1-6]|<img|<ul|<ol|<li)(.+)$/gim, "<p>$1</p>")
// 处理换行
.replace(/\n/g, "<br>")
return html
}
// 保存
const onsubmit = async (evt: TM.FORM_SUBMIT_RESULT) => {
// console.log("校验结果:", evt)
// 将Markdown格式的description转换为HTML
const htmlDescription = convertMarkdownToHtml(form.description)
console.log("原始Markdown:", form.description)
console.log("转换后HTML:", htmlDescription)
const submitData = {
...form,
description: htmlDescription, // 使用转换后的HTML
coverImage: JSON.parse(form.coverImage[0].response).data.url,
}
console.log("提交的数据:", submitData)
@@ -451,7 +328,6 @@ const onsubmit = async (evt: TM.FORM_SUBMIT_RESULT) => {
price: storePrice(item.price),
}
}),
description: htmlDescription, // 使用转换后的HTML
coverImage: JSON.parse(form.coverImage[0].response).data.url,
}
uni.showLoading({ title: "提交中..." })
+5 -42
View File
@@ -107,8 +107,8 @@
</view>
</z-paging>
<view class="fixed bottom-80rpx left-40rpx flex-col gap-2">
<tm-button class="px-2" size="s" :linear="['to right', '#0779FD', '#62abff']" @click="addGoods">新增商品</tm-button>
<tm-button class="px-2" size="s" :linear="['to right', '#0779FD', '#62abff']" @click="categaryManage">分类管理</tm-button>
<tm-button class="px-2" size="m" :linear="['to right', '#0779FD', '#62abff']" @click="addGoods">新增商品</tm-button>
<tm-button class="px-2" size="m" :linear="['to right', '#0779FD', '#62abff']" @click="categaryManage">分类管理</tm-button>
</view>
</crud-layout>
</template>
@@ -120,6 +120,7 @@ import { hooks } from "@/sheep"
import { filePath } from "@/sheep/utils/file"
import { onLoad, onShow } from "@dcloudio/uni-app"
import { displayPrice } from "@/sheep/utils/formatMoney"
import { useGoodsManage } from "./use-goods-manage"
const state = reactive<any>({
params: {
@@ -137,6 +138,8 @@ const query = (page: number, pagesize: number) => {
pageHook.startPage(page, pagesize, false, state.params)
}
const { addGoods, editGoods, deleteGoods, doGoodsDetail, categaryManage } = useGoodsManage(pageRef)
onLoad(async () => {
await getGoodsCategory()
})
@@ -167,46 +170,6 @@ const search = () => {
const inputClear = () => {
pageRef.value && pageRef.value.reload(false)
}
const doGoodsDetail = (id: string) => {
if (!id) return uni.showToast({ title: "商品id不存在", icon: "none" })
uni.navigateTo({
url: `/pages/prosperity-shop/goods-detail?id=${id}`,
})
}
// 新增商品
const addGoods = () => {
uni.navigateTo({ url: `/pages/prosperity-shop/goods-form` })
}
// 编辑商品
const editGoods = (id: string) => {
if (!id) return uni.showToast({ title: "商品id不存在", icon: "none" })
uni.navigateTo({ url: `/pages/prosperity-shop/goods-form?id=${id}` })
}
// 删除商品
const deleteGoods = (id: string) => {
if (!id) return uni.showToast({ title: "商品id不存在", icon: "none" })
uni.showModal({
title: "确定删除商品吗?",
content: "",
success: async (res) => {
if (res.confirm) {
const { code } = await prosperityShopApi.deleteGoods(id)
if (code != 0) return uni.showToast({ title: "删除失败", icon: "error" })
uni.showToast({ title: "删除成功", icon: "none" })
pageRef.value && pageRef.value.reload(false)
}
},
})
}
// 分类管理
const categaryManage = () => {
uni.navigateTo({ url: "/pages/prosperity-shop/goods-categary" })
}
</script>
<style scoped lang="scss">
+4 -39
View File
@@ -1,5 +1,5 @@
<template>
<crud-layout mainIsScroll bg="/static/page/blue-bg.png" @scroll="scroll">
<crud-layout mainIsScroll bg="/static/page/blue-bg.png" @scroll="onScroll">
<view class="w-full h-400rpx relative">
<image :src="filePath(state.detail.images)" class="w-full h-full bg-#eeeeee" mode="aspectFill" />
<view class="absolute w-full h-full top-0 left-0" :style="filterStyle"></view>
@@ -88,21 +88,13 @@
</template>
<script setup lang="ts">
import { computed, reactive, ref } from "vue"
import { reactive } from "vue"
import { prosperityShopApi } from "@/sheep/api"
import { onLoad } from "@dcloudio/uni-app"
import { filePath } from "@/sheep/utils/file"
import treeToList from "@/sheep/utils/tree-to-list"
import { displayPrice } from "@/sheep/utils/formatMoney"
interface scrollDetail {
deltaX: number
deltaY: number
scrollHeight: number
scrollLeft: number
scrollTop: number
scrollWidth: number
}
import { useShopDetailScroll } from "./use-shop-detail-scroll"
const state = reactive<any>({
id: "",
@@ -111,7 +103,7 @@ const state = reactive<any>({
curCategoryIndex: 0,
goodsList: [],
})
const scrollDetail = ref<scrollDetail>({} as scrollDetail)
const { scrollDetail, onScroll, filterStyle, searchStyle } = useShopDetailScroll()
onLoad(async (option: any) => {
if (!option.id) return uni.showToast({ title: "参数错误", icon: "error" })
@@ -155,33 +147,6 @@ const doCategory = async (index: number) => {
uni.hideLoading()
}
const scroll = (e: any) => {
scrollDetail.value = e.detail
}
const filterStyle = computed(() => {
const scrollY = scrollDetail.value.scrollTop
const blur = Math.min(1, scrollY / 400) * 20
return {
backdropFilter: `blur(${blur}rpx)`,
}
})
const searchStyle = computed(() => {
const minWidth = 10
const scrollY = scrollDetail.value.scrollTop
const scrollHeight = scrollDetail.value.scrollHeight
const scrollWidth = scrollDetail.value.scrollWidth
const calcWidth = (scrollY / scrollHeight) * scrollWidth
if (isNaN(calcWidth)) return { width: `${minWidth}%` }
if (calcWidth < 2) return { width: `${calcWidth + minWidth}%` }
return {
width: `${calcWidth + minWidth}%`,
border: `2rpx solid #1879ff`,
opacity: (calcWidth / 100) * 2,
}
})
const doGoodsDetail = (id: string) => {
if (!id) return uni.showToast({ title: "商品id不存在", icon: "none" })
uni.navigateTo({
@@ -0,0 +1,33 @@
import { prosperityShopApi } from "@/sheep/api"
import { ref } from "vue"
export function useCategaryManage() {
const categoryList = ref<any>([])
// 获取商品分类
const getGoodsCategory = async () => {
const { code, data, msg } = await prosperityShopApi.getGoodsCategory()
if (code != 0) return uni.showToast({ title: msg || "商品分类获取失败", icon: "none" })
categoryList.value = data
}
// 删除分类
const delCategary = async (id: string) => {
if (!id) return uni.showToast({ title: "分类id不存在", icon: "none" })
uni.showModal({
title: "确定删除分类吗?",
content: "",
success: async (res) => {
if (res.confirm) {
uni.showLoading({ title: "正在删除" })
const { code, msg } = await prosperityShopApi.delCategary(id)
if (code != 0) return uni.showToast({ title: msg || "删除失败", icon: "none" })
uni.showToast({ title: "删除成功", icon: "none" })
getGoodsCategory()
uni.hideLoading()
}
},
})
}
return { categoryList, getGoodsCategory, delCategary }
}
@@ -0,0 +1,42 @@
import { prosperityShopApi } from "@/sheep/api"
export function useGoodsManage(pageRef) {
// 新增商品
const addGoods = () => {
uni.navigateTo({ url: `/pages/prosperity-shop/goods-form` })
}
// 编辑商品
const editGoods = (id: string) => {
if (!id) return uni.showToast({ title: "商品id不存在", icon: "none" })
uni.navigateTo({ url: `/pages/prosperity-shop/goods-form?id=${id}` })
}
// 删除商品
const deleteGoods = (id: string) => {
if (!id) return uni.showToast({ title: "商品id不存在", icon: "none" })
uni.showModal({
title: "确定删除商品吗?",
content: "",
success: async (res) => {
if (res.confirm) {
const { code } = await prosperityShopApi.deleteGoods(id)
if (code != 0) return uni.showToast({ title: "删除失败", icon: "error" })
uni.showToast({ title: "删除成功", icon: "none" })
pageRef.value && pageRef.value.reload(false)
}
},
})
}
// 查看详情
const doGoodsDetail = (id: string) => {
if (!id) return uni.showToast({ title: "商品id不存在", icon: "none" })
uni.navigateTo({
url: `/pages/prosperity-shop/goods-detail?id=${id}`,
})
}
// 分类管理
const categaryManage = () => {
uni.navigateTo({ url: "/pages/prosperity-shop/categary-manage" })
}
return { addGoods, editGoods, deleteGoods, doGoodsDetail, categaryManage }
}
@@ -0,0 +1,45 @@
import { computed, ref } from "vue"
interface scrollDetail {
deltaX: number
deltaY: number
scrollHeight: number
scrollLeft: number
scrollTop: number
scrollWidth: number
}
export function useShopDetailScroll() {
const scrollDetail = ref<scrollDetail>({} as scrollDetail)
const onScroll = (e: any) => {
scrollDetail.value = e.detail
}
const filterStyle = computed(() => {
if (!scrollDetail.value.scrollTop) return {}
const scrollY = scrollDetail.value.scrollTop
const blur = Math.min(1, scrollY / 400) * 10
return {
backdropFilter: `blur(${blur}rpx)`,
}
})
const searchStyle = computed(() => {
if (!scrollDetail.value.scrollTop) return {}
const minWidth = 10
const scrollY = scrollDetail.value.scrollTop
const scrollHeight = scrollDetail.value.scrollHeight
const scrollWidth = scrollDetail.value.scrollWidth
const calcWidth = Math.min((scrollY / scrollHeight) * scrollWidth, scrollWidth)
if (isNaN(calcWidth)) return { width: `${minWidth}%` }
if (calcWidth < 2) return { width: `${calcWidth + minWidth}%` }
return {
width: `${calcWidth + minWidth}%`,
border: `2rpx solid #1879ff`,
opacity: (calcWidth / 100) * 6,
}
})
return { scrollDetail, onScroll, filterStyle, searchStyle }
}
+10
View File
@@ -95,6 +95,16 @@ onMounted(() => {
})
function goPage(item) {
// 访客邀约功能正在开发中
if (item.name === 'visitor') {
uni.showToast({
title: '访客邀约功能正在开发中',
icon: 'none',
duration: 2000
})
return
}
if (item.url) {
uni.navigateTo({ url: item.url })
}
+5 -5
View File
@@ -84,8 +84,8 @@ const state = reactive<{
userInfo: {},
menuList: [
{ text: "扫一扫", icon: "/static/icon/scan.png", router: "" },
{ text: "钱包管理", icon: "/static/icon/service_3.png", router: "/pages/wallet/list" },
{ text: "人脸管理", icon: "/static/icon/service_5.png", router: "/pages/human-face/list" },
{ text: "我的钱包", icon: "/static/icon/service_3.png", router: "/pages/wallet/list" },
{ text: "我的人脸", icon: "/static/icon/service_5.png", router: "/pages/human-face/list" },
// { text: "车辆管理", icon: "/static/icon/service_6.png", router: "/pages/car/userCar/list" },
{ text: "清除缓存", icon: "/static/icon/clear-storage.png", router: "" },
],
@@ -426,14 +426,14 @@ onLoad(async () => {
await userApi.userRole()
await isMerchant()
await isMallMchUser()
// 获取存储中的用户信息
storage.getUserInfo()
})
const isMerchant = async () => {
const { data } = await userApi.isMerchant()
const isMallMchUser = async () => {
const { data } = await userApi.isMallMchUser()
if (data)
state.menuList.unshift({ text: "共富商城商品管理", icon: "/static/icon/service_4.png", router: "/pages/prosperity-shop/goods-manage" })
}
+7 -1
View File
@@ -60,12 +60,18 @@ export default {
},
})
},
// 判断是否是商户角色
// 判断是否是合作商家商户角色
isMerchant: (): Promise<any> =>
request({
url: "/admin/role/isMchUser",
method: "GET",
}),
// 判断是否是共富商城商户角色
isMallMchUser: (): Promise<any> =>
request({
url: "/admin/role/isMallMchUser",
method: "GET",
}),
// 判断用户移动端角色
userRole: () =>
request({
@@ -3,17 +3,18 @@
<view v-for="item in nodeData" :key="item.id">
<view
class="tree-item"
:style="item.id == selectedNode.id ? { backgroundColor: '#4b93ff', color: '#fff' } : { color: '#000' }"
:style="item.id == selectedNode.id ? { borderColor: '#1879ff' } : { color: '#000' }"
@click.stop="toCurrentNode(item)"
>
<image class="tree-item-icon" :src="filePath(item.picUrl)" />
<text>{{ item.name }}</text>
</view>
<shop-categary-tree
<node-tree
v-model="selectedNode"
v-if="item.children && item.children.length"
:node-data="item.children"
:style="{ marginLeft: `${getLevel() * 60}rpx` }"
:style="{ marginLeft: `${getLevel(item.level) * 60}rpx` }"
@currentNode="(v) => emit('currentNode', v)"
/>
</view>
@@ -21,14 +22,10 @@
</template>
<script setup lang="ts">
import ShopCategaryTree from "./shop-categary-tree.vue"
import { filePath } from "../utils/file"
import NodeTree from "./node-tree.vue"
defineProps({
nodeData: {
type: Array<any>,
default: () => [],
},
})
withDefaults(defineProps<{ nodeData: any[] }>(), { nodeData: () => [] })
const emit = defineEmits(["currentNode"])
const selectedNode = defineModel<any>("modelValue")
@@ -37,19 +34,26 @@ const toCurrentNode = (item) => {
selectedNode.value = item
}
const getLevel = () => {
return 1
const getLevel = (level) => {
return level
}
</script>
<style scoped>
.tree-container {
padding: 0 40rpx;
overflow: hidden;
}
.tree-item {
border-bottom: 1rpx solid #e4e7ed;
border: 4rpx solid #e4e7ed77;
margin-bottom: 16rpx;
display: flex;
padding: 10rpx 0 10rpx 20rpx;
align-items: center;
padding: 10rpx 20rpx;
border-radius: 10rpx;
}
.tree-item-icon {
width: 40rpx;
height: 40rpx;
margin-right: 10rpx;
}
</style>
@@ -1,9 +1,9 @@
<template>
<view class="max-h-[60vh] overflow-y-auto mb-20rpx">
<shop-categary-tree v-model="selectedNode" :node-data="categoryTree" />
<view v-if="categoryTree.length === 0" class="text-center text-[#888] py-[40rpx]">暂无数据</view>
<view class="mb-20rpx">
<node-tree v-model="selectedNode" :node-data="categoryTree" />
<view v-if="categoryTree.length === 0" class="text-center text-#888 py-40rpx">暂无数据</view>
</view>
<view class="px-[20rpx] py-2 flex justify-between sticky bottom-0 bg-white">
<view class="px-20rpx py-2 flex justify-between sticky bottom-0 bg-white">
<tm-button skin="thin" block @click="emit('cancel')">取消</tm-button>
&nbsp;&nbsp;
<tm-button block :disabled="!selectedNode" @click="onConfirm">确定</tm-button>
@@ -12,7 +12,7 @@
<script setup lang="ts">
import { ref } from "vue"
import ShopCategaryTree from "@/sheep/components/shop-categary-tree.vue"
import NodeTree from "@/sheep/components/node-tree.vue"
defineProps({
categoryTree: {
@@ -31,5 +31,3 @@ function onConfirm() {
emit("confirm", { ...selectedNode, categoryId: selectedNode.value.id, categoryName: selectedNode.value.name })
}
</script>
<style scoped></style>