初始化
parent
654a427e48
commit
755c150220
|
@ -10,12 +10,15 @@ declare module 'vue' {
|
||||||
NAvatar: typeof import('naive-ui')['NAvatar']
|
NAvatar: typeof import('naive-ui')['NAvatar']
|
||||||
NBadge: typeof import('naive-ui')['NBadge']
|
NBadge: typeof import('naive-ui')['NBadge']
|
||||||
NButton: typeof import('naive-ui')['NButton']
|
NButton: typeof import('naive-ui')['NButton']
|
||||||
|
NCarousel: typeof import('naive-ui')['NCarousel']
|
||||||
NCascader: typeof import('naive-ui')['NCascader']
|
NCascader: typeof import('naive-ui')['NCascader']
|
||||||
NCheckbox: typeof import('naive-ui')['NCheckbox']
|
NCheckbox: typeof import('naive-ui')['NCheckbox']
|
||||||
NCheckboxGroup: typeof import('naive-ui')['NCheckboxGroup']
|
NCheckboxGroup: typeof import('naive-ui')['NCheckboxGroup']
|
||||||
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
||||||
NDatePicker: typeof import('naive-ui')['NDatePicker']
|
NDatePicker: typeof import('naive-ui')['NDatePicker']
|
||||||
NDropdown: typeof import('naive-ui')['NDropdown']
|
NDropdown: typeof import('naive-ui')['NDropdown']
|
||||||
|
NEllipsis: typeof import('naive-ui')['NEllipsis']
|
||||||
|
NEmpty: typeof import('naive-ui')['NEmpty']
|
||||||
NFor: typeof import('naive-ui')['NFor']
|
NFor: typeof import('naive-ui')['NFor']
|
||||||
NForm: typeof import('naive-ui')['NForm']
|
NForm: typeof import('naive-ui')['NForm']
|
||||||
NFormItem: typeof import('naive-ui')['NFormItem']
|
NFormItem: typeof import('naive-ui')['NFormItem']
|
||||||
|
|
|
@ -1,52 +1,55 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import type { FormItemRule } from "naive-ui";
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
const message = useMessage()
|
const message = useMessage();
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore();
|
||||||
const userInfo = userStore.userInfo
|
const userInfo = userStore.userInfo;
|
||||||
const isVisible = ref(false)
|
const isVisible = ref(false);
|
||||||
|
|
||||||
const rules = ref({
|
const rules = ref({
|
||||||
name: [
|
name: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
validator(value: string) {
|
validator(rule: FormItemRule, value: string) {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return new Error('请填写姓名')
|
return new Error("请填写姓名");
|
||||||
|
} else if (!/^[\u4E00-\u9FA5·]{2,16}$/.test(value)) {
|
||||||
|
return new Error("输入正确的姓名");
|
||||||
}
|
}
|
||||||
else if (!/^[\u4E00-\u9FA5·]{2,16}$/.test(value)) {
|
return true;
|
||||||
return new Error('输入正确的姓名')
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
},
|
},
|
||||||
trigger: 'blur',
|
trigger: "blur",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
idCard: [
|
idCard: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
validator(value: string) {
|
validator(rule: FormItemRule, value: string) {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return new Error('请填身份证号')
|
return new Error("请填身份证号");
|
||||||
|
} else if (
|
||||||
|
!/(^\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(^\d{6}(18|19|20)\d{2}(0\d|10|11|12)([0-2]\d|30|31)\d{3}([\dX])$)/i.test(
|
||||||
|
value
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return new Error("输入正确的身份证号");
|
||||||
}
|
}
|
||||||
else if (!/(^\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(^\d{6}(18|19|20)\d{2}(0\d|10|11|12)([0-2]\d|30|31)\d{3}([\dX])$)/i.test(value)) {
|
return true;
|
||||||
return new Error('输入正确的身份证号')
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
},
|
},
|
||||||
trigger: 'blur',
|
trigger: "blur",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
});
|
||||||
|
|
||||||
const ruleForm = ref({
|
const ruleForm = ref({
|
||||||
name: '',
|
name: "",
|
||||||
idCard: '',
|
idCard: "",
|
||||||
userId: userInfo.userId,
|
userId: userInfo.userId,
|
||||||
})
|
});
|
||||||
const formRef = ref(null)
|
const formRef = ref(null);
|
||||||
async function saveInfo(e: MouseEvent) {
|
async function saveInfo(e: MouseEvent) {
|
||||||
e.preventDefault()
|
e.preventDefault();
|
||||||
formRef.value?.validate(async (errors) => {
|
formRef.value?.validate(async (errors) => {
|
||||||
if (!errors) {
|
if (!errors) {
|
||||||
// try{
|
// try{
|
||||||
|
@ -61,27 +64,33 @@ async function saveInfo(e: MouseEvent) {
|
||||||
// }catch(err) {
|
// }catch(err) {
|
||||||
// console.log('err',err)
|
// console.log('err',err)
|
||||||
// }
|
// }
|
||||||
await request.post('/system/user/updateIdCard', ruleForm.value)
|
try {
|
||||||
await userStore.getUserInfo()
|
const res = await request.post("/system/user/updateIdCard", ruleForm.value);
|
||||||
isVisible.value = false
|
if (res.code === 200) {
|
||||||
|
await userStore.getUserInfo();
|
||||||
|
isVisible.value = false;
|
||||||
|
} else {
|
||||||
|
message.warning(res.msg);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
});
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onCloseModel() {
|
function onCloseModel() {
|
||||||
isVisible.value = false
|
isVisible.value = false;
|
||||||
ruleForm.value.name = ''
|
ruleForm.value.name = "";
|
||||||
ruleForm.value.idCard = ''
|
ruleForm.value.idCard = "";
|
||||||
}
|
}
|
||||||
defineExpose({
|
defineExpose({
|
||||||
isVisible,
|
isVisible,
|
||||||
})
|
});
|
||||||
// 生命周期钩子
|
// 生命周期钩子
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// initFormData()
|
// initFormData()
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -94,14 +103,25 @@ onMounted(() => {
|
||||||
>
|
>
|
||||||
<n-form ref="formRef" :model="ruleForm" :rules="rules">
|
<n-form ref="formRef" :model="ruleForm" :rules="rules">
|
||||||
<n-form-item path="name" label="姓名">
|
<n-form-item path="name" label="姓名">
|
||||||
<n-input v-model:value="ruleForm.name" placeholder="请输入姓名" @keydown.enter.prevent />
|
<n-input
|
||||||
|
v-model:value="ruleForm.name"
|
||||||
|
placeholder="请输入姓名"
|
||||||
|
@keydown.enter.prevent
|
||||||
|
/>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item path="idCard" label="身份证号">
|
<n-form-item path="idCard" label="身份证号">
|
||||||
<n-input v-model:value="ruleForm.idCard" placeholder="请输入身份证号" @keydown.enter.prevent />
|
<n-input
|
||||||
|
v-model:value="ruleForm.idCard"
|
||||||
|
placeholder="请输入身份证号"
|
||||||
|
@keydown.enter.prevent
|
||||||
|
/>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</n-form>
|
</n-form>
|
||||||
<div class="flex justify-center items-center ">
|
<div class="flex justify-center items-center">
|
||||||
<button class="w-1/2 flex justify-center mx-1 bg-[#213df5] py-3 rounded-lg align-middle color-[#fff] border-0 cursor-pointer" @click="saveInfo">
|
<button
|
||||||
|
class="w-1/2 flex justify-center mx-1 bg-[#213df5] py-3 rounded-lg align-middle text-[#fff] border-0 cursor-pointer"
|
||||||
|
@click="saveInfo"
|
||||||
|
>
|
||||||
实名认证
|
实名认证
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,328 +0,0 @@
|
||||||
<script setup>
|
|
||||||
import { ref } from 'vue'
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
height: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
dataList: {
|
|
||||||
type: Array,
|
|
||||||
default: () => [],
|
|
||||||
},
|
|
||||||
userInfo: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({}),
|
|
||||||
},
|
|
||||||
productId: {
|
|
||||||
type: Number,
|
|
||||||
default: 0,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const message = useMessage()
|
|
||||||
|
|
||||||
// 获取评论列表
|
|
||||||
const commentList = ref([])
|
|
||||||
async function getCommentList() {
|
|
||||||
try {
|
|
||||||
const res = await request.get(`/WorkFlowComment/comment/${props.productId}`)
|
|
||||||
for (let i = 0; i < res.data.length; i++) {
|
|
||||||
res.data[i].isShowInput = false
|
|
||||||
res.data[i].isShowSend = false
|
|
||||||
if (res.data[i].contentList.length > 0) {
|
|
||||||
for (let j = 0; j < res.data[i].contentList.length; j++) {
|
|
||||||
res.data[i].contentList[j].isShowInput = false
|
|
||||||
res.data[i].contentList[j].isShowSend = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
commentList.value = res.data
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
console.log(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
getCommentList()
|
|
||||||
// const $props = defineProps(['headUrl', 'dataList', 'height'])
|
|
||||||
// const $emit = defineEmits(['sendMessage'])
|
|
||||||
// const props = defineProps
|
|
||||||
const isShowSend = ref(false)
|
|
||||||
const publicWord = ref('')
|
|
||||||
// const message = useMessage()
|
|
||||||
|
|
||||||
function handleBlur(ele) {
|
|
||||||
ele.isShowInput = false
|
|
||||||
// 默认评价
|
|
||||||
if (!ele) {
|
|
||||||
isShowSend.value = !!publicWord.value
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ele.isShowSend = !!ele.word
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 发布评论
|
|
||||||
const commentParams = ref({
|
|
||||||
content: '',
|
|
||||||
parentId: '',
|
|
||||||
userId: props.userInfo.userId,
|
|
||||||
workFlowId: props.productId,
|
|
||||||
})
|
|
||||||
//
|
|
||||||
async function sendMessage(data, ele) {
|
|
||||||
debugger
|
|
||||||
try {
|
|
||||||
if (commentParams.value.content) {
|
|
||||||
const res = await request.post('WorkFlowComment/comment', commentParams.value)
|
|
||||||
if (res.code === 200) {
|
|
||||||
message.success('评论成功!')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
message.error('评论不能为空!')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
console.log(error)
|
|
||||||
}
|
|
||||||
// if(!publicWord.value){
|
|
||||||
// return message.info(
|
|
||||||
// '请输入评论',
|
|
||||||
// { duration: 3000 }
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
if (!data) {
|
|
||||||
// $props.dataList.push({
|
|
||||||
// headUrl: 'https://avatars.githubusercontent.com/u/19239641?s=60&v=4', // 头像
|
|
||||||
// name: '星辰流连', // 名称
|
|
||||||
// id: 6,
|
|
||||||
// des: publicWord.value, // 评论
|
|
||||||
// time: '2025/02/08 14:52:06', // 时间
|
|
||||||
// isAuthor: false, // 是否是作者
|
|
||||||
// isShowInput: false, // 是否显示回复框
|
|
||||||
// isFocus: true, // 是否点赞
|
|
||||||
// focusNum: 2, // 点赞数量
|
|
||||||
// word: '', // 评论语
|
|
||||||
// isShowSend: false, // 是否显示发送按钮
|
|
||||||
// })
|
|
||||||
|
|
||||||
publicWord.value = ''
|
|
||||||
// isShowSend.value = false
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
data.push({
|
|
||||||
headUrl: 'https://avatars.githubusercontent.com/u/19239641?s=60&v=4', // 头像
|
|
||||||
name: '星辰流连', // 名称
|
|
||||||
id: 7,
|
|
||||||
des: ele.word, // 评论
|
|
||||||
time: '2025/02/08 14:52:06', // 时间
|
|
||||||
isAuthor: false, // 是否是作者
|
|
||||||
isShowInput: false, // 是否显示回复框
|
|
||||||
isFocus: true, // 是否点赞
|
|
||||||
focusNum: 2, // 点赞数量
|
|
||||||
word: '', // 评论语
|
|
||||||
isShowSend: false, // 是否显示发送按钮
|
|
||||||
})
|
|
||||||
|
|
||||||
ele.word = ''
|
|
||||||
ele.isShowInput = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 发送数据
|
|
||||||
$emit('sendMessage', data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 目前点赞都在这边,注意区分
|
|
||||||
function handleFocus(item) {
|
|
||||||
item.isFocus = !item.isFocus
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleMessage(item) {
|
|
||||||
debugger
|
|
||||||
if (!item.isShowInput) {
|
|
||||||
item.word = ''
|
|
||||||
}
|
|
||||||
item.isShowInput = !item.isShowInput
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDel(ele, item) {
|
|
||||||
const index = item.findIndex(el => ele.id === el.id)
|
|
||||||
item.splice(index, 1)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="base-comment">
|
|
||||||
<!-- 头部评论框 -->
|
|
||||||
<div class="flex justify-between mb-4">
|
|
||||||
<div v-if="props.userInfo.avatar" class="w-11 h-11 rounded-full mr-4 border border-[#2d28ff] border-solid">
|
|
||||||
<img class="w-10 h-10 rounded-full p-1" alt="avatar" :src="props.userInfo.avatar">
|
|
||||||
</div>
|
|
||||||
<div class="input-wrap flex items-center leading-normal rounded-lg text-sm bg-[#f2f5f9] px-4 py-2 flex-1">
|
|
||||||
<NConfigProvider inline-theme-disabled class="w-full">
|
|
||||||
<n-input
|
|
||||||
v-model:value="commentParams.content"
|
|
||||||
type="textarea"
|
|
||||||
:autosize="{ minRows: 1 }"
|
|
||||||
placeholder="善语结善缘,恶言伤人心~"
|
|
||||||
@focus="isShowSend = true"
|
|
||||||
@blur="handleBlur"
|
|
||||||
/>
|
|
||||||
</NConfigProvider>
|
|
||||||
<div
|
|
||||||
class="mx-4 flex-shrink-0 text-sm cursor-pointer text-gray-400 hover:text-gray-900"
|
|
||||||
:class="{ 'text-gray-900': commentParams.content }"
|
|
||||||
@click="sendMessage"
|
|
||||||
>
|
|
||||||
<span v-if="isShowSend">发送sendMessage</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 评论列表 -->
|
|
||||||
<NInfiniteScroll :style="{ height: `${height}px` }" :distance="10" @load="handleLoad">
|
|
||||||
<div v-for="(item, index) in commentList" :key="index">
|
|
||||||
<!-- 主评论 -->
|
|
||||||
<div class="flex text-sm">
|
|
||||||
<div class="w-10 h-10 rounded-full mr-3 flex-shrink-0">
|
|
||||||
<img :src="item.userAvatar" class="w-full h-full rounded-full">
|
|
||||||
</div>
|
|
||||||
<div class="w-full">
|
|
||||||
<div class="mt-[10px] mb-[7px] font-medium text-gray-700">
|
|
||||||
{{ item.userName }}
|
|
||||||
</div>
|
|
||||||
<div class="leading-[1.25]">
|
|
||||||
{{ item.content }}
|
|
||||||
</div>
|
|
||||||
<div class="mt-1 flex items-center justify-between">
|
|
||||||
<span class="text-xs text-gray-400">{{ item.createTime }}</span>
|
|
||||||
<div class="text-xs text-gray-400 flex items-center cursor-pointer">
|
|
||||||
<div class="flex items-center mr-4">
|
|
||||||
<img v-if="item.focus" src="@/assets/img/heart.png" class="w-4 h-4 mr-[3px]">
|
|
||||||
<img v-else src="@/assets/img/heart.png" class="w-4 h-4 mr-[3px]" @click="handleFocus(item)">
|
|
||||||
<span class="align-middle">{{ item.likeNum }}</span>
|
|
||||||
</div>
|
|
||||||
<span class="mr-4" @click="handleMessage(item)">回复</span>
|
|
||||||
<span @click="handleDel(item, dataList)">删除</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 评论回复框 -->
|
|
||||||
<div v-if="item.isShowInput" class="input-wrap mt-[10px] flex items-center rounded-lg text-sm bg-[#f2f5f9] px-3 py-2">
|
|
||||||
<NConfigProvider inline-theme-disabled class="w-full">
|
|
||||||
<n-input
|
|
||||||
v-model:value="item.word"
|
|
||||||
type="textarea"
|
|
||||||
:autosize="{ minRows: 1 }"
|
|
||||||
:placeholder="`回复: @${item.userName}`"
|
|
||||||
@focus="item.isShowSend = true"
|
|
||||||
@blur="handleBlur(item)"
|
|
||||||
/>
|
|
||||||
</NConfigProvider>
|
|
||||||
<div
|
|
||||||
class="mx-4 flex-shrink-0 text-sm cursor-pointer text-gray-400 hover:text-gray-900"
|
|
||||||
:class="{ 'text-gray-900': item.word }"
|
|
||||||
@click="sendMessage(item.children, item)"
|
|
||||||
>
|
|
||||||
<span v-if="item.isShowSend">发送sendMessage</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 子评论 -->
|
|
||||||
<div class="pl-[52px]">
|
|
||||||
<div v-for="(ele, subIndex) in item.contentList" :key="subIndex" class="flex text-sm mt-4">
|
|
||||||
<div class="w-6 h-6 rounded-full mr-3 flex-shrink-0">
|
|
||||||
<img src="https://avatars.githubusercontent.com/u/20943608?s=60&v=4" class="w-full h-full rounded-full">
|
|
||||||
</div>
|
|
||||||
<div class="w-full">
|
|
||||||
<div class="mb-[7px] font-medium text-gray-700 flex items-center">
|
|
||||||
{{ ele.userName }}
|
|
||||||
<div v-if="ele.userAvatar" class="ml-2 px-1 py-[3px] text-xs leading-[12px] text-white rounded bg-gradient-to-r from-[#2d28ff] to-[#1a7dff]">
|
|
||||||
作者
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center">
|
|
||||||
<span>回复</span>
|
|
||||||
<span class="text-[#1880ff] mr-[6px]">@{{ ele.userName }}</span>
|
|
||||||
{{ ele.des }}
|
|
||||||
</div>
|
|
||||||
<div class="mt-1 flex items-center justify-between">
|
|
||||||
<span class="text-xs text-gray-400">{{ ele.createTime }}</span>
|
|
||||||
<div class="text-xs text-gray-400 flex items-center cursor-pointer">
|
|
||||||
<div class="flex items-center mr-4">
|
|
||||||
<img v-if="ele.isFocus" src="@/assets/img/heart.png" class="w-4 h-4 mr-[3px]" @click="handleFocus(ele, 'cancel')">
|
|
||||||
<img v-else src="@/assets/img/heart.png" class="w-4 h-4 mr-[3px]" @click="handleFocus(ele, 'add')">
|
|
||||||
<span class="align-middle">1</span>
|
|
||||||
</div>
|
|
||||||
<span class="mr-4" @click="handleMessage(ele)">回复</span>
|
|
||||||
<span @click="handleDel(ele, item.contentList)">删除</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 子评论回复框 -->
|
|
||||||
<div v-if="ele.isShowInput" class="input-wrap mt-[10px] flex items-center rounded-lg text-sm bg-[#f2f5f9] px-3 py-2">
|
|
||||||
<NConfigProvider inline-theme-disabled class="w-full">
|
|
||||||
<n-input
|
|
||||||
v-model:value="ele.word"
|
|
||||||
type="textarea"
|
|
||||||
:autosize="{ minRows: 1 }"
|
|
||||||
:placeholder="`回复: @${ele.userName}`"
|
|
||||||
@focus="ele.isShowSend = true"
|
|
||||||
@blur="handleBlur(ele)"
|
|
||||||
/>
|
|
||||||
</NConfigProvider>
|
|
||||||
<div
|
|
||||||
class="mx-4 flex-shrink-0 text-sm cursor-pointer text-gray-400 hover:text-gray-900"
|
|
||||||
:class="{ 'text-gray-900': ele.word }"
|
|
||||||
@click="sendMessage(item.children, ele)"
|
|
||||||
>
|
|
||||||
<span v-if="ele.isShowSend">发送sendMessage</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 加载更多 -->
|
|
||||||
<div class="text-sm text-gray-400 text-center py-[46px] pb-[118px]">
|
|
||||||
暂时没有更多评论
|
|
||||||
</div>
|
|
||||||
</NInfiniteScroll>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.input-wrap {
|
|
||||||
:deep(.n-input) {
|
|
||||||
--n-padding-left: 0 !important;
|
|
||||||
--n-border: 0 !important;
|
|
||||||
--n-border-hover: 0 !important;
|
|
||||||
--n-border-focus: 0 !important;
|
|
||||||
--n-box-shadow-focus: unset;
|
|
||||||
--n-padding-vertical: 0px !important;
|
|
||||||
}
|
|
||||||
&::hover {
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
&:active,
|
|
||||||
&:focus {
|
|
||||||
background-color: #f2f5f9;
|
|
||||||
|
|
||||||
border: 0 !important;
|
|
||||||
}
|
|
||||||
background-color: #f2f5f9;
|
|
||||||
}
|
|
||||||
.n-input--textarea {
|
|
||||||
background-color: #f2f5f9;
|
|
||||||
}
|
|
||||||
.n-input:not(.n-input--disabled).n-input--foucs {
|
|
||||||
background-color: #f2f5f9 !important;
|
|
||||||
}
|
|
||||||
.n-input--focus {
|
|
||||||
background-color: #f2f5f9 !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,8 +1,8 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// import { NConfigProvider, NInfiniteScroll, NInput } from 'naive-ui'
|
// import { NConfigProvider, NInfiniteScroll, NInput } from 'naive-ui'
|
||||||
import {
|
import {
|
||||||
ThumbsUp,
|
ThumbsUp
|
||||||
} from 'lucide-vue-next'
|
} from 'lucide-vue-next';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
height: {
|
height: {
|
||||||
|
@ -45,30 +45,48 @@ const sortType = ref(1)
|
||||||
// 获取列表
|
// 获取列表
|
||||||
const urlList = ref({
|
const urlList = ref({
|
||||||
workflow: '/WorkFlowComment/comment?',
|
workflow: '/WorkFlowComment/comment?',
|
||||||
|
pictrue: '/imageComment/comment?',
|
||||||
|
model: '/ModelComment/comment?',
|
||||||
})
|
})
|
||||||
// 点赞
|
// 点赞
|
||||||
const likeList = ref({ // 点赞
|
const likeList = ref({
|
||||||
workflow: '/WorkFlowComment/commentLike?',
|
workflow: '/WorkFlowComment/commentLike?',
|
||||||
|
pictrue: '/imageComment/commentLike?',
|
||||||
|
model:'ModelComment/commentLike?'
|
||||||
})
|
})
|
||||||
// 发送评论
|
// 发送评论
|
||||||
const sendMessageList = ref({ // 点赞
|
const sendMessageList = ref({
|
||||||
workflow: '/WorkFlowComment/comment',
|
workflow: '/WorkFlowComment/comment',
|
||||||
|
pictrue: '/imageComment/comment',
|
||||||
|
model: '/ModelComment/comment',
|
||||||
})
|
})
|
||||||
// 删除
|
// 删除
|
||||||
const deleteList = ref({ // 点赞
|
const deleteList = ref({
|
||||||
workflow: '/WorkFlowComment/commentDelete?',
|
workflow: '/WorkFlowComment/commentDelete?',
|
||||||
|
pictrue: '/imageComment/commentDelete?',
|
||||||
|
model: '/ModelComment/commentDelete?',
|
||||||
})
|
})
|
||||||
// 查条数
|
// 查条数
|
||||||
const commentNumUrl = ref({ // 点赞
|
const commentNumUrl = ref({
|
||||||
workflow: '/WorkFlowComment/commentCount?workFlowId',
|
workflow: '/WorkFlowComment/commentCount?workFlowId',
|
||||||
|
model: '/ModelComment/commentCount?modelId',
|
||||||
})
|
})
|
||||||
|
|
||||||
const commentCount = ref(0)
|
const commentCount = ref(0)
|
||||||
|
|
||||||
// 评论列表
|
// 评论列表
|
||||||
const commentList = ref([])
|
const commentList = ref([])
|
||||||
async function getCommentList() {
|
async function getCommentList() {
|
||||||
try {
|
try {
|
||||||
const res = await request.get(`${urlList.value[props.type]}commentId=${props.detailsInfo.id}&sortType=${sortType.value}`)
|
let url = ''
|
||||||
|
if(props.type === 'workflow'){
|
||||||
|
url = `${urlList.value[props.type]}commentId=${props.detailsInfo.id}&sortType=${sortType.value}`
|
||||||
|
}else if(props.type === 'pictrue'){
|
||||||
|
url = `${urlList.value[props.type]}imageId=${props.detailsInfo.id}&sortType=${sortType.value}`
|
||||||
|
}else{
|
||||||
|
url = `${urlList.value[props.type]}modelId=${props.detailsInfo.id}&sortType=${sortType.value}`
|
||||||
|
}
|
||||||
|
const res = await request.get(url)
|
||||||
for (let i = 0; i < res.data.length; i++) {
|
for (let i = 0; i < res.data.length; i++) {
|
||||||
res.data[i].isShowInput = false
|
res.data[i].isShowInput = false
|
||||||
res.data[i].isShowSend = false
|
res.data[i].isShowSend = false
|
||||||
|
@ -104,6 +122,11 @@ async function sendMessage(ele, index) {
|
||||||
commentParams.value.content = ele.word
|
commentParams.value.content = ele.word
|
||||||
commentParams.value.parentId = commentList.value[index].commentId
|
commentParams.value.parentId = commentList.value[index].commentId
|
||||||
commentParams.value.replyUserId = ele.commentId
|
commentParams.value.replyUserId = ele.commentId
|
||||||
|
if(props.type === 'pictrue'){
|
||||||
|
commentParams.value.modelImageId = props.detailsInfo.id
|
||||||
|
}else if(props.type === 'model'){
|
||||||
|
commentParams.value.modelId = props.detailsInfo.id
|
||||||
|
}
|
||||||
const res = await request.post(sendMessageList.value[props.type], commentParams.value)
|
const res = await request.post(sendMessageList.value[props.type], commentParams.value)
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
message.success('评论成功!')
|
message.success('评论成功!')
|
||||||
|
@ -126,7 +149,12 @@ async function sendMessage(ele, index) {
|
||||||
commentParams.value.parentId = ''
|
commentParams.value.parentId = ''
|
||||||
commentParams.value.content = publicWord.value
|
commentParams.value.content = publicWord.value
|
||||||
commentParams.value.replyUserId = ''
|
commentParams.value.replyUserId = ''
|
||||||
const res = await request.post('WorkFlowComment/comment', commentParams.value)
|
if(props.type === 'pictrue'){
|
||||||
|
commentParams.value.modelImageId = props.detailsInfo.id
|
||||||
|
}else if(props.type === 'model'){
|
||||||
|
commentParams.value.modelId = props.detailsInfo.id
|
||||||
|
}
|
||||||
|
const res = await request.post(sendMessageList.value[props.type], commentParams.value)
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
message.success('评论成功!')
|
message.success('评论成功!')
|
||||||
publicWord.value = ''
|
publicWord.value = ''
|
||||||
|
@ -145,8 +173,10 @@ async function sendMessage(ele, index) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//查询条数
|
||||||
async function getCommentNum() {
|
async function getCommentNum() {
|
||||||
try {
|
if(props.type !== 'pictrue'){
|
||||||
|
try {
|
||||||
const res = await request.get(`${commentNumUrl.value[props.type]}=${props.detailsInfo.id}`)
|
const res = await request.get(`${commentNumUrl.value[props.type]}=${props.detailsInfo.id}`)
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
commentCount.value = res.data
|
commentCount.value = res.data
|
||||||
|
@ -155,10 +185,12 @@ async function getCommentNum() {
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
getCommentNum()
|
getCommentNum()
|
||||||
|
|
||||||
// 点赞/取消点赞
|
// 点赞/取消点赞
|
||||||
async function handleFocus(item) {
|
async function handleFocus(item:any) {
|
||||||
await request.get(`${likeList.value[props.type]}commentId=${item.commentId}`)
|
await request.get(`${likeList.value[props.type]}commentId=${item.commentId}`)
|
||||||
if (item.isLike === 0) {
|
if (item.isLike === 0) {
|
||||||
item.isLike = 1
|
item.isLike = 1
|
||||||
|
@ -173,14 +205,14 @@ async function handleFocus(item) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示回复框
|
// 显示回复框
|
||||||
function handleMessage(item) {
|
function handleMessage(item:any) {
|
||||||
if (!item.isShowInput) {
|
if (!item.isShowInput) {
|
||||||
item.word = ''
|
item.word = ''
|
||||||
}
|
}
|
||||||
item.isShowInput = !item.isShowInput
|
item.isShowInput = !item.isShowInput
|
||||||
}
|
}
|
||||||
// 删除评论
|
// 删除评论
|
||||||
async function handleDel(item) {
|
async function handleDel(item:any) {
|
||||||
try {
|
try {
|
||||||
const res = await request.get(`${deleteList.value[props.type]}commentId=${item.commentId}`)
|
const res = await request.get(`${deleteList.value[props.type]}commentId=${item.commentId}`)
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
|
@ -207,11 +239,11 @@ function changeType(type: string) {
|
||||||
<div class="left text-[20px] mr-2">
|
<div class="left text-[20px] mr-2">
|
||||||
讨论
|
讨论
|
||||||
</div>
|
</div>
|
||||||
<div class="text-[#999]">
|
<div class="text-[#999]" v-if="props.type !== 'pictrue'">
|
||||||
{{ commentCount }}
|
{{ commentCount }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center" v-if="props.type !== 'pictrue'">
|
||||||
<div class="cursor-pointer" :class="sortType === 0 ? '' : 'text-[#999]'" @click="changeType(0)">
|
<div class="cursor-pointer" :class="sortType === 0 ? '' : 'text-[#999]'" @click="changeType(0)">
|
||||||
最热
|
最热
|
||||||
</div>|
|
</div>|
|
||||||
|
@ -245,7 +277,6 @@ function changeType(type: string) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<NInfiniteScroll :style="{ height: `${height}px` }" :distance="10" @load="handleLoad">
|
|
||||||
<div
|
<div
|
||||||
v-for="(item, index) in commentList"
|
v-for="(item, index) in commentList"
|
||||||
:key="index"
|
:key="index"
|
||||||
|
@ -347,7 +378,6 @@ function changeType(type: string) {
|
||||||
<div class="no-more">
|
<div class="no-more">
|
||||||
暂时没有更多评论
|
暂时没有更多评论
|
||||||
</div>
|
</div>
|
||||||
</NInfiniteScroll>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -363,8 +393,8 @@ function changeType(type: string) {
|
||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
border: 1px solid #2d28ff;
|
border: 1px solid #2d28ff;
|
||||||
img {
|
img {
|
||||||
width: 40px;
|
width: 100%;
|
||||||
height: 40px;
|
height: 100%;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -424,8 +454,8 @@ function changeType(type: string) {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
img {
|
img {
|
||||||
width: 40px;
|
width: 100%;
|
||||||
height: 40px;
|
height: 100%;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -490,8 +520,8 @@ function changeType(type: string) {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
img {
|
img {
|
||||||
width: 24px;
|
width: 100%;
|
||||||
height: 24px;
|
height: 100%;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,153 +1,171 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// 输入框搜索
|
// 输入框搜索
|
||||||
import { headerRole } from '@/constants/index'
|
import { headerRole } from "@/constants/index";
|
||||||
import {
|
import {
|
||||||
Bell,
|
Bell,
|
||||||
CirclePlus,
|
CirclePlus,
|
||||||
GraduationCap,
|
GraduationCap,
|
||||||
HardDriveUpload,
|
HardDriveUpload,
|
||||||
Image,
|
Image,
|
||||||
Monitor,
|
Monitor,
|
||||||
Workflow,
|
Workflow
|
||||||
} from 'lucide-vue-next'
|
} from "lucide-vue-next";
|
||||||
import { NConfigProvider, NMessageProvider } from 'naive-ui'
|
import { NConfigProvider, NMessageProvider } from "naive-ui";
|
||||||
|
|
||||||
// import { AtCircle } from '@vicons/ionicons5'
|
// import { AtCircle } from '@vicons/ionicons5'
|
||||||
import { NIcon } from 'naive-ui'
|
import { NIcon } from "naive-ui";
|
||||||
import { onMounted, ref, watch } from 'vue'
|
import { onMounted, ref, watch } from "vue";
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from "vue-router";
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute();
|
||||||
const router = useRouter()
|
const router = useRouter();
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore();
|
||||||
const modalStore = useModalStore()
|
const modalStore = useModalStore();
|
||||||
const currentUseRoute = ref('')
|
const currentUseRoute = ref("");
|
||||||
const isShowPublishPicture = ref<boolean>(false)
|
const isShowPublishPicture = ref<boolean>(false);
|
||||||
const PublishPictureRef = ref<Payment | null>(null)
|
const PublishPictureRef = ref<Payment | null>(null);
|
||||||
const publishPicture = ref({
|
const publishPicture = ref({
|
||||||
title: '',
|
title: "",
|
||||||
tags: [],
|
tags: [],
|
||||||
description: '',
|
description: "",
|
||||||
imagePaths: [],
|
imagePaths: [],
|
||||||
})
|
});
|
||||||
watch(
|
watch(
|
||||||
() => route.path, // 监听 route.path 的变化
|
() => route.path, // 监听 route.path 的变化
|
||||||
(newPath) => {
|
(newPath) => {
|
||||||
currentUseRoute.value = newPath
|
currentUseRoute.value = newPath;
|
||||||
},
|
},
|
||||||
{ immediate: true }, // 立即执行一次
|
{ immediate: true } // 立即执行一次
|
||||||
)
|
);
|
||||||
function hasItem(path: string, list: any) {
|
function hasItem(path: string, list: any) {
|
||||||
return !list.includes(path)
|
return !list.includes(path);
|
||||||
}
|
}
|
||||||
const searchText = ref('')
|
const searchText = ref("");
|
||||||
function onSearch(value: any) {
|
function onSearch(value: any) {
|
||||||
console.log('搜索:', value)
|
console.log("搜索:", value);
|
||||||
// 执行搜索逻辑
|
// 执行搜索逻辑
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用户下拉选项
|
// 用户下拉选项
|
||||||
const notificationOptions = [
|
const notificationOptions = [
|
||||||
{
|
{
|
||||||
label: '系统通知',
|
label: "系统通知",
|
||||||
key: 'system',
|
key: "system",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '互动消息',
|
label: "互动消息",
|
||||||
key: 'interaction',
|
key: "interaction",
|
||||||
},
|
},
|
||||||
]
|
];
|
||||||
function renderIcon(icon: Component) {
|
function renderIcon(icon: Component) {
|
||||||
return () => {
|
return () => {
|
||||||
return h(NIcon, null, {
|
return h(NIcon, null, {
|
||||||
default: () => h(icon),
|
default: () => h(icon),
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
// 发布下拉选项
|
// 发布下拉选项
|
||||||
const publishOptions = [
|
const publishOptions = [
|
||||||
{
|
{
|
||||||
label: '模型',
|
label: "模型",
|
||||||
key: 'publish-model',
|
key: "publish-model",
|
||||||
icon: renderIcon(HardDriveUpload),
|
icon: renderIcon(HardDriveUpload),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '图片',
|
label: "图片",
|
||||||
key: 'picture',
|
key: "picture",
|
||||||
icon: renderIcon(Image),
|
icon: renderIcon(Image),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '工作流',
|
label: "工作流",
|
||||||
key: 'publish-workflow',
|
key: "publish-workflow",
|
||||||
icon: renderIcon(Workflow),
|
icon: renderIcon(Workflow),
|
||||||
},
|
},
|
||||||
]
|
];
|
||||||
const userOptions = ref([
|
const userOptions = ref([
|
||||||
{
|
{
|
||||||
label: '我的模型',
|
label: "我的模型",
|
||||||
key: 'model',
|
key: "model",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '我的作品',
|
label: "我的作品",
|
||||||
key: 'project',
|
key: "project",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '我的点赞',
|
label: "我的点赞",
|
||||||
key: 'like',
|
key: "like",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '账号设置',
|
label: "账号设置",
|
||||||
key: 'userSettings',
|
key: "userSettings",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '退出登录',
|
label: "退出登录",
|
||||||
key: 'logout',
|
key: "logout",
|
||||||
},
|
},
|
||||||
])
|
]);
|
||||||
|
|
||||||
// 用户下拉选项
|
// 用户下拉选项
|
||||||
async function handleUserSelect(key: string) {
|
async function handleUserSelect(key: string) {
|
||||||
if (key === 'logout') {
|
if (key === "logout") {
|
||||||
try {
|
try {
|
||||||
await request.post('/logout')
|
await request.post("/logout");
|
||||||
userStore.logout()
|
userStore.logout();
|
||||||
navigateTo('/model-square')
|
navigateTo("/model-square");
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
console.error("Logout failed:", error);
|
||||||
console.error('Logout failed:', error)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 发布下拉选项
|
// 发布下拉选项
|
||||||
async function handlePublishSelect(key: string) {
|
async function handlePublishSelect(key: string) {
|
||||||
if (key === 'picture') {
|
if (key === "picture") {
|
||||||
isShowPublishPicture.value = true
|
isShowPublishPicture.value = true;
|
||||||
if (PublishPictureRef.value) {
|
if (PublishPictureRef.value) {
|
||||||
PublishPictureRef.value.isVisible = true
|
PublishPictureRef.value.isVisible = true;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
const baseUrl = window.location.origin;
|
||||||
router.push({
|
window.open(`${baseUrl}/${key}?type=add`, "_blank", "noopener,noreferrer");
|
||||||
path: `/${key}`,
|
// router.push({
|
||||||
query: {
|
// path: `/${key}`,
|
||||||
type: 'add',
|
// query: {
|
||||||
},
|
// type: 'add',
|
||||||
})
|
// },
|
||||||
|
// })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function closePublishImg() {
|
function closePublishImg() {
|
||||||
isShowPublishPicture.value = false
|
isShowPublishPicture.value = false;
|
||||||
if (PublishPictureRef.value) {
|
if (PublishPictureRef.value) {
|
||||||
PublishPictureRef.value.isVisible = false
|
PublishPictureRef.value.isVisible = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleLogin() {
|
function handleLogin() {
|
||||||
modalStore.showLoginModal()
|
modalStore.showLoginModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
const msgList = ref([]);
|
||||||
})
|
async function getAllMessage() {
|
||||||
|
try {
|
||||||
|
const res = await request.get("/advice/getAllMsg");
|
||||||
|
if (res.code == 200) {
|
||||||
|
msgList.value = res.data;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getAllMessage();
|
||||||
|
|
||||||
|
// 跳转到消息详情
|
||||||
|
function toDetail(){
|
||||||
|
const baseUrl = window.location.origin
|
||||||
|
window.open(`${baseUrl}/message`, '_blank', 'noopener,noreferrer')
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -201,14 +219,70 @@ onMounted(() => {
|
||||||
</NDropdown>
|
</NDropdown>
|
||||||
|
|
||||||
<!-- Notifications -->
|
<!-- Notifications -->
|
||||||
<NDropdown :options="notificationOptions" trigger="click">
|
<!-- :options="notificationOptions" -->
|
||||||
|
<!-- <NDropdown trigger="click">
|
||||||
<NBadge :value="5" :max="99" processing>
|
<NBadge :value="5" :max="99" processing>
|
||||||
<NButton text circle>
|
<NButton text circle>
|
||||||
<Bell class="h-5 w-5 mr-1" />
|
<Bell class="h-5 w-5 mr-1 relative" />
|
||||||
|
<div class="absolute top-2 right-1 border-solid border-2 border-[#f0f0f0] py-2">
|
||||||
|
<div class="border-b-solid border-b-2 border-b-[#f0f0f0]">
|
||||||
|
通知
|
||||||
|
</div>
|
||||||
|
<div v-for="(item,index) in 3">
|
||||||
|
1111
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</NButton>
|
</NButton>
|
||||||
</NBadge>
|
</NBadge>
|
||||||
</NDropdown>
|
</NDropdown> -->
|
||||||
|
<div
|
||||||
|
class="p-1 bg-[#f1f1f6] rounded-full flex items-center justify-center relative group"
|
||||||
|
>
|
||||||
|
<Bell class="h-5 w-5relative cursor-pointer" @mouseenter="getAllMessage" />
|
||||||
|
<div class="pt-4 absolute top-5 -right-4 hidden group-hover:block">
|
||||||
|
<div
|
||||||
|
class="border-solid border border-[#f0f0f0] py-2 w-[300px] rounded-lg bg-white"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="border-b-solid border-b border-b-[#f0f0f0] p-2 text-sm font-bold"
|
||||||
|
>
|
||||||
|
通知
|
||||||
|
</div>
|
||||||
|
<div class="p-2 max-h-[300px] overflow-y-auto">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in msgList"
|
||||||
|
:key="index"
|
||||||
|
class="flex items-center my-2 cursor-pointer"
|
||||||
|
>
|
||||||
|
<div class="flex-1">
|
||||||
|
<n-ellipsis
|
||||||
|
style="max-width: 250px"
|
||||||
|
:tooltip="false"
|
||||||
|
class="font-bold text-gray-600"
|
||||||
|
>
|
||||||
|
{{ item.content }}
|
||||||
|
</n-ellipsis>
|
||||||
|
<div class="text-[12px] text-gray-400">{{ item.createTime }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="w-4 flex h-[100%] items-center justify-center">
|
||||||
|
<div
|
||||||
|
class="w-2 h-2 bg-[#ea5049] rounded"
|
||||||
|
v-if="item.isRead === '0'"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="my-4 text-gray-500 text-sm text-center">
|
||||||
|
更多通知可点击查看全部~
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<div class="bg-[#4c79ee] w-[270px] h-10 flex items-center justify-center rounded-lg text-white cursor-pointer" @click="toDetail">
|
||||||
|
查看全部
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<!-- User -->
|
<!-- User -->
|
||||||
<div class="min-w-10">
|
<div class="min-w-10">
|
||||||
<client-only>
|
<client-only>
|
||||||
|
@ -239,7 +313,13 @@ onMounted(() => {
|
||||||
<div>
|
<div>
|
||||||
<NConfigProvider>
|
<NConfigProvider>
|
||||||
<NMessageProvider>
|
<NMessageProvider>
|
||||||
<Publish-picture v-if="isShowPublishPicture" ref="PublishPictureRef" :form-data="publishPicture" @close-publish-img="closePublishImg" />
|
<Publish-picture
|
||||||
|
v-if="isShowPublishPicture"
|
||||||
|
ref="PublishPictureRef"
|
||||||
|
type="add"
|
||||||
|
:form-data="publishPicture"
|
||||||
|
@close-publish-img="closePublishImg"
|
||||||
|
/>
|
||||||
</NMessageProvider>
|
</NMessageProvider>
|
||||||
</NConfigProvider>
|
</NConfigProvider>
|
||||||
</div>
|
</div>
|
||||||
|
@ -247,7 +327,7 @@ onMounted(() => {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.header-btn-primary {
|
.header-btn-primary {
|
||||||
@apply border border-solid border-[#3162ff] px-1 py-1 text-[#3162ff] rounded-md hover:text-[#3162ff];
|
@apply border border-solid border-[#3162ff] px-1 py-1 text-[#3162ff] rounded-md hover:text-[#3162ff];
|
||||||
}
|
}
|
||||||
.header-btn {
|
.header-btn {
|
||||||
|
|
|
@ -0,0 +1,175 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-wrap justify-center">
|
||||||
|
<div class="grid grid-cols-2 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-7 gap-4 p-4">
|
||||||
|
<div
|
||||||
|
v-for="item in dataList"
|
||||||
|
:key="item.id"
|
||||||
|
class="relative rounded-lg overflow-hidden"
|
||||||
|
>
|
||||||
|
<!-- 图片 -->
|
||||||
|
<div class="relative h-[300px] overflow-hidden rounded-lg" @click="toDetail(item)">
|
||||||
|
<img
|
||||||
|
:src="item.surfaceUrl"
|
||||||
|
class="w-full h-full object-cover rounded-lg cursor-pointer ransform transition-transform duration-300 hover:scale-110"
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 左上角标签 -->
|
||||||
|
<div
|
||||||
|
class="absolute top-2 left-2 bg-black/50 text-white text-xs px-2 py-1 rounded"
|
||||||
|
>
|
||||||
|
{{ item.type }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 底部数据统计 -->
|
||||||
|
<div
|
||||||
|
class="absolute bottom-0 left-0 right-0 flex items-center gap-2 p-2 text-xs text-white"
|
||||||
|
>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<component :is="Play" class="h-[14px] w-[14px] text-white menu-icon m-1" />
|
||||||
|
{{ item.reals || 0 }}
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<component
|
||||||
|
:is="Download"
|
||||||
|
class="h-[14px] w-[14px] text-white menu-icon m-1"
|
||||||
|
/>
|
||||||
|
{{ item.numbers || 0 }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 作者信息条 -->
|
||||||
|
<div class="mt-1 px-2 py-1">
|
||||||
|
<div>
|
||||||
|
{{ item.modelName }}
|
||||||
|
</div>
|
||||||
|
<div class="flex mt-2">
|
||||||
|
<img :src="item.avatar" class="w-5 h-5 rounded-full mr-2" alt="" />
|
||||||
|
<span class="text-sm text-gray-500 truncate">{{ item.nickName }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
ref="loadingTrigger"
|
||||||
|
class="h-20 w-[1000px] text-center text-gray-500 flex justify-center items-center"
|
||||||
|
>
|
||||||
|
<div v-if="loading">加载中...</div>
|
||||||
|
<div v-if="finished && dataList.length >= 20">没有更多数据了</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Download, Play } from "lucide-vue-next";
|
||||||
|
import { nextTick, onMounted, onUnmounted, ref } from "vue";
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
const finished = ref(false);
|
||||||
|
const total = ref(0); // 总条数
|
||||||
|
const loadingTrigger = ref(null);
|
||||||
|
const observer = ref<IntersectionObserver | null>(null);
|
||||||
|
const props = defineProps({
|
||||||
|
params: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const listParams = ref({
|
||||||
|
...props.params,
|
||||||
|
pageNumber: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
function initPageNUm() {
|
||||||
|
listParams.value.pageNumber = 1;
|
||||||
|
finished.value = false; // 重置加载完成状态
|
||||||
|
listParams.value = Object.assign({}, listParams.value, props.params);
|
||||||
|
getDataList();
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataList = ref([]);
|
||||||
|
async function getDataList() {
|
||||||
|
if (loading.value || finished.value) return;
|
||||||
|
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await request.post("/model/modelSquare", { ...listParams.value });
|
||||||
|
if (res.code === 200) {
|
||||||
|
// 如果是第一页,直接赋值,否则追加数据
|
||||||
|
if (listParams.value.pageNumber === 1) {
|
||||||
|
dataList.value = res.data.list;
|
||||||
|
} else {
|
||||||
|
dataList.value = [...dataList.value, ...res.data.list];
|
||||||
|
}
|
||||||
|
|
||||||
|
total.value = res.data.total; // 假设接口返回了总条数
|
||||||
|
|
||||||
|
// 判断是否加载完所有数据
|
||||||
|
if (dataList.value.length >= total.value) {
|
||||||
|
finished.value = true;
|
||||||
|
}
|
||||||
|
// 自动增加页码
|
||||||
|
listParams.value.pageNumber++;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
dataList.value = [];
|
||||||
|
finished.value = true;
|
||||||
|
console.log(err);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getDataList();
|
||||||
|
|
||||||
|
|
||||||
|
// 跳转详情
|
||||||
|
function toDetail(item:any){
|
||||||
|
router.push(`/model-details/${item.id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener("scroll", topedRefresh);
|
||||||
|
observer.value = new IntersectionObserver(
|
||||||
|
([entry]) => {
|
||||||
|
if (entry.isIntersecting && !loading.value && !finished.value) {
|
||||||
|
getDataList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
threshold: 0.1,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (loadingTrigger.value) {
|
||||||
|
observer.value.observe(loadingTrigger.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener("scroll", topedRefresh);
|
||||||
|
if (observer.value) {
|
||||||
|
observer.value.disconnect();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function topedRefresh() {
|
||||||
|
if (import.meta.client) {
|
||||||
|
await nextTick();
|
||||||
|
window.scrollTo({
|
||||||
|
top: 0,
|
||||||
|
behavior: "smooth",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
initPageNUm();
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
initPageNUm,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
|
@ -1,12 +1,12 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {
|
import {
|
||||||
CircleAlert,
|
CircleAlert,
|
||||||
Download,
|
Download,
|
||||||
EllipsisVertical,
|
EllipsisVertical,
|
||||||
Play,
|
Play
|
||||||
} from 'lucide-vue-next'
|
} from 'lucide-vue-next';
|
||||||
import { ref } from 'vue'
|
import { nextTick, ref } from 'vue';
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
item: {
|
item: {
|
||||||
|
@ -36,6 +36,8 @@ function toDetails() {
|
||||||
else if (props.currentType === '1') {
|
else if (props.currentType === '1') {
|
||||||
// console.log('object', 111);
|
// console.log('object', 111);
|
||||||
router.push(`/workflow-details/${props.item.id}`)
|
router.push(`/workflow-details/${props.item.id}`)
|
||||||
|
}else if(props.currentType === '2'){
|
||||||
|
onEditPicture()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,11 +67,11 @@ function handleSelect(event: Event, key: string) {
|
||||||
handleDelete()
|
handleDelete()
|
||||||
}
|
}
|
||||||
else if (key === 'edit') {
|
else if (key === 'edit') {
|
||||||
if (props.currentType === '2') {
|
if (props.currentType === '2') { // 图片
|
||||||
getPublishPicture()
|
getPublishPicture()
|
||||||
showPublishImg()
|
showPublishImg()
|
||||||
}
|
}
|
||||||
else if (props.currentType === '1') {
|
else if (props.currentType === '1') { // 工作流
|
||||||
router.push({
|
router.push({
|
||||||
path: `/publish-workflow`,
|
path: `/publish-workflow`,
|
||||||
query: {
|
query: {
|
||||||
|
@ -78,6 +80,15 @@ function handleSelect(event: Event, key: string) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
else if (props.currentType === '0') { // 模型
|
||||||
|
router.push({
|
||||||
|
path: `/publish-model`,
|
||||||
|
query: {
|
||||||
|
type: 'edit',
|
||||||
|
id: props.item.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,6 +171,35 @@ function closePublishImg() {
|
||||||
PublishPictureRef.value.isVisible = false
|
PublishPictureRef.value.isVisible = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 显示图片的编辑
|
||||||
|
const isShowEditorPicture = ref(false)
|
||||||
|
interface EditUserInfoType {
|
||||||
|
isVisible: boolean
|
||||||
|
}
|
||||||
|
const editUserInfoRef = ref<EditUserInfoType | null>(null)
|
||||||
|
function onEditPicture() {
|
||||||
|
isShowEditorPicture.value = true
|
||||||
|
nextTick(()=>{
|
||||||
|
if(editUserInfoRef.value){
|
||||||
|
editUserInfoRef.value.isVisible = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function closeEditorPicture(){
|
||||||
|
isShowEditorPicture.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateLike(type:number){
|
||||||
|
if (props.item.isLike === 1) {
|
||||||
|
props.item.isLike = 0;
|
||||||
|
props.item.likeNum -= 1;
|
||||||
|
} else {
|
||||||
|
props.item.isLike = 1;
|
||||||
|
props.item.likeNum += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -310,13 +350,16 @@ function closePublishImg() {
|
||||||
:src="item.userAvatar"
|
:src="item.userAvatar"
|
||||||
alt=""
|
alt=""
|
||||||
>
|
>
|
||||||
<span>{{ item.userName }}</span>
|
<span>{{ item.userName }} </span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<NConfigProvider>
|
<NConfigProvider>
|
||||||
<NMessageProvider>
|
<NMessageProvider>
|
||||||
<Publish-picture v-if="isShowPublishPicture" ref="PublishPictureRef" :form-data="publishPictureData" @close-publish-img="closePublishImg" />
|
<div v-if="isShowEditorPicture">
|
||||||
|
<PictureDetail @close-editor-picture="closeEditorPicture" ref="editUserInfoRef" :item="item" @update-like="updateLike"/>
|
||||||
|
</div>
|
||||||
|
<Publish-picture v-if="isShowPublishPicture" type="edit" ref="PublishPictureRef" :form-data="publishPictureData" @close-publish-img="closePublishImg" />
|
||||||
</NMessageProvider>
|
</NMessageProvider>
|
||||||
</NConfigProvider>
|
</NConfigProvider>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,190 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { commonApi } from "@/api/common";
|
||||||
|
import { CopyOutline, Heart } from "@vicons/ionicons5";
|
||||||
|
import { Download, PartyPopper } from "lucide-vue-next";
|
||||||
|
import { NConfigProvider, NMessageProvider } from "naive-ui";
|
||||||
|
import { ref } from "vue";
|
||||||
|
const emit = defineEmits(["updateLike"]);
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const userInfo = userStore.userInfo;
|
||||||
|
const message = useMessage();
|
||||||
|
const props = defineProps({
|
||||||
|
item: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const dataInfo = ref({});
|
||||||
|
async function getDetail() {
|
||||||
|
if (props.item && props.item.id) {
|
||||||
|
const res = await request.get(`/image/detail?id=${props.item.id}`);
|
||||||
|
if (res.code === 200) {
|
||||||
|
dataInfo.value = res.data;
|
||||||
|
getDictType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getDetail();
|
||||||
|
const tagsList = ref([]);
|
||||||
|
// 获取图片标签
|
||||||
|
async function getDictType() {
|
||||||
|
try {
|
||||||
|
const res = await commonApi.dictType({ type: "image_label" });
|
||||||
|
if (res.code === 200 && res.data.length > 0) {
|
||||||
|
for (let i = 0; i < res.data.length; i++) {
|
||||||
|
for (let j = 0; j < dataInfo.value.tags.length; j++) {
|
||||||
|
if (res.data[i].dictValue === dataInfo.value.tags[j]) {
|
||||||
|
tagsList.value.push(res.data[i].dictLabel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onShowModel() {
|
||||||
|
// ruleForm.value.nickName = userInfo.nickName
|
||||||
|
// ruleForm.value.avatar = userInfo.avatar
|
||||||
|
// ruleForm.value.brief = userInfo.brief
|
||||||
|
// ruleForm.value.userId = userInfo.userId
|
||||||
|
}
|
||||||
|
|
||||||
|
const isVisible = ref(false);
|
||||||
|
function onCloseModel() {
|
||||||
|
isVisible.value = false;
|
||||||
|
}
|
||||||
|
const commentHeight = ref(200);
|
||||||
|
|
||||||
|
//点赞
|
||||||
|
async function onLike() {
|
||||||
|
try {
|
||||||
|
const res = await request.get(`/image/imageLike?id=${props.item.id}`);
|
||||||
|
if (res.code === 200) {
|
||||||
|
// emit('updateLike')
|
||||||
|
if (dataInfo.value.isLike === 1) {
|
||||||
|
dataInfo.value.isLike = 0;
|
||||||
|
dataInfo.value.likeNum -= 1;
|
||||||
|
message.success("取消点赞成功");
|
||||||
|
} else {
|
||||||
|
dataInfo.value.isLike = 1;
|
||||||
|
dataInfo.value.likeNum += 1;
|
||||||
|
message.success("点赞成功");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defineExpose({
|
||||||
|
isVisible,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NModal
|
||||||
|
v-model:show="isVisible"
|
||||||
|
:on-after-leave="onCloseModel"
|
||||||
|
:on-after-enter="onShowModel"
|
||||||
|
preset="card"
|
||||||
|
style="width: auto; border-radius: 10px; box-sizing: border-box"
|
||||||
|
:mask-closable="false"
|
||||||
|
>
|
||||||
|
<div class="p-2 w-[700px]" style="box-sizing: border-box">
|
||||||
|
<div class="flex w-full">
|
||||||
|
<div class="flex-1 p-1">
|
||||||
|
<img class="w-full h-[300px]" :src="dataInfo.imagePaths" alt="" />
|
||||||
|
<div class="flex mt-2">
|
||||||
|
<div
|
||||||
|
class="mr-2 w-40 px2 py-3 flex items-center justify-center text-[#8a4200] cursor-pointer rounded-lg bg-gradient-to-r from-[#ffe9c8] to-[#ffd264]"
|
||||||
|
>
|
||||||
|
<Download size="16" class="mr-1" />
|
||||||
|
下载无水印原图
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="flex items-center bg-[#eceef4] px2 py-3 w-20 justify-center cursor-pointer rounded-lg"
|
||||||
|
>
|
||||||
|
<n-icon
|
||||||
|
class="mr-2"
|
||||||
|
size="20"
|
||||||
|
:color="dataInfo.isLike === 1 ? '#ff0000' : '#ccc'"
|
||||||
|
@click="onLike"
|
||||||
|
>
|
||||||
|
<Heart />
|
||||||
|
</n-icon>
|
||||||
|
{{ dataInfo.likeNum }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg-[#f6f9fe] p-2 text-[12px] mt-2 rounded-lg">
|
||||||
|
作者添加了水印,成为会员后可下载无水印原图。
|
||||||
|
<span class="text-[#4a69ed] cursor-pointer">直接下载</span>带水印的图片
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1 p-1">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<img class="w-10 h-10 rounded-full mr-2" :src="dataInfo.userAvatar" alt="" />
|
||||||
|
<div>
|
||||||
|
{{ dataInfo.userName }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-[20px] mt-3">
|
||||||
|
{{ dataInfo.title }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-[14px] mt-2 text-gray-400">
|
||||||
|
{{ dataInfo.description }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-[14px] mt-2 text-gray-400">
|
||||||
|
{{ dataInfo.createTime }}
|
||||||
|
</div>
|
||||||
|
<div class="flex mt-2">
|
||||||
|
<div
|
||||||
|
class="flex w-[100px] items-center bg-[#eceef4] px2 py-3 justify-center cursor-pointer rounded-lg mr-2"
|
||||||
|
>
|
||||||
|
<n-icon class="mr-2" size="20" color="#ccc">
|
||||||
|
<CopyOutline />
|
||||||
|
</n-icon>
|
||||||
|
复制全部
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="flex w-[200px] items-center px2 py-3 bg-[#416af6] text-white rounded-lg justify-center cursor-pointer"
|
||||||
|
>
|
||||||
|
<PartyPopper size="16" class="mr-1" />一键生图
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="p-1 flex flex-wrap">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in tagsList"
|
||||||
|
:key="index"
|
||||||
|
class="text-[#5d79ba] bg-[#ecf2fe] p-2 text-[12px] font-bold mr-2 mt-2 w-auto rounded-lg"
|
||||||
|
>
|
||||||
|
{{ item }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-4">
|
||||||
|
<div style="padding: 20px">
|
||||||
|
<NConfigProvider>
|
||||||
|
<NMessageProvider>
|
||||||
|
<BaseComment
|
||||||
|
v-if="dataInfo.id"
|
||||||
|
type="pictrue"
|
||||||
|
:height="commentHeight"
|
||||||
|
:details-info="dataInfo"
|
||||||
|
/>
|
||||||
|
</NMessageProvider>
|
||||||
|
</NConfigProvider>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</NModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
|
@ -0,0 +1,210 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-wrap justify-center">
|
||||||
|
<div class="grid grid-cols-2 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-7 gap-4 p-4">
|
||||||
|
<div
|
||||||
|
v-for="item in dataList"
|
||||||
|
:key="item.id"
|
||||||
|
class="relative rounded-lg overflow-hidden"
|
||||||
|
>
|
||||||
|
<!-- 图片 -->
|
||||||
|
<div class="relative h-[300px] overflow-hidden rounded-lg group">
|
||||||
|
<img
|
||||||
|
:src="item.avatar"
|
||||||
|
class="w-full h-full object-cover rounded-lg cursor-pointer"
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="px-2 py-1 absolute top-0 left-0 w-full h-full hidden group-hover:block bg-black bg-opacity-40 cursor-pointer"
|
||||||
|
>
|
||||||
|
<div class="flex justify-between w-full">
|
||||||
|
<div class="flex mt-2">
|
||||||
|
<div class="w-5 h-5 border border-white mr-2 rounded-full">
|
||||||
|
<img :src="item.avatar" class="w-full h-full rounded-full" alt="" />
|
||||||
|
</div>
|
||||||
|
<span class="text-sm text-white truncate">{{ item.nickName }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="p-2 rounded-full bg-white flex items-center cursor-pointer">
|
||||||
|
<n-icon
|
||||||
|
class="mr-2"
|
||||||
|
size="20"
|
||||||
|
:color="item.isLike === 0 ? '#ccc' : '#ff0000'"
|
||||||
|
@click="onLike(item)"
|
||||||
|
>
|
||||||
|
<Heart />
|
||||||
|
</n-icon>
|
||||||
|
{{ item.likeNum }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 左上角标签 -->
|
||||||
|
<!-- <div
|
||||||
|
class="absolute top-2 left-2 bg-black/50 text-white text-xs px-2 py-1 rounded"
|
||||||
|
>
|
||||||
|
{{ item.type }}
|
||||||
|
</div> -->
|
||||||
|
|
||||||
|
<!-- 底部数据统计 -->
|
||||||
|
<!-- <div
|
||||||
|
class="absolute bottom-0 left-0 right-0 flex items-center gap-2 p-2 text-xs text-white"
|
||||||
|
>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<component :is="Play" class="h-[14px] w-[14px] text-white menu-icon m-1" />
|
||||||
|
{{ item.reals || 0 }}
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<component
|
||||||
|
:is="Download"
|
||||||
|
class="h-[14px] w-[14px] text-white menu-icon m-1"
|
||||||
|
/>
|
||||||
|
{{ item.numbers || 0 }}
|
||||||
|
</div>
|
||||||
|
</div> -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 作者信息条 -->
|
||||||
|
<!-- <div class="mt-1 px-2 py-1">
|
||||||
|
<div>
|
||||||
|
{{ item.modelName }}
|
||||||
|
</div>
|
||||||
|
<div class="flex mt-2">
|
||||||
|
<img :src="item.avatar" class="w-5 h-5 rounded-full mr-2" alt="" />
|
||||||
|
<span class="text-sm text-gray-500 truncate">{{ item.nickName }}</span>
|
||||||
|
</div>
|
||||||
|
</div> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
ref="loadingTrigger"
|
||||||
|
class="h-20 w-[1000px] text-center text-gray-500 flex justify-center items-center"
|
||||||
|
>
|
||||||
|
<div v-if="loading">加载中...</div>
|
||||||
|
<div v-if="finished && dataList.length >= 20">没有更多数据了</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Heart } from "@vicons/ionicons5";
|
||||||
|
|
||||||
|
import { nextTick, onMounted, onUnmounted, ref } from "vue";
|
||||||
|
const loading = ref(false);
|
||||||
|
const finished = ref(false);
|
||||||
|
const total = ref(0); // 总条数
|
||||||
|
const loadingTrigger = ref(null);
|
||||||
|
const observer = ref<IntersectionObserver | null>(null);
|
||||||
|
const message = useMessage();
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
params: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const listParams = ref({
|
||||||
|
...props.params,
|
||||||
|
pageNumber: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
function initPageNUm() {
|
||||||
|
listParams.value.pageNumber = 1;
|
||||||
|
finished.value = false; // 重置加载完成状态
|
||||||
|
listParams.value = Object.assign({}, listParams.value, props.params);
|
||||||
|
getDataList();
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataList = ref([]);
|
||||||
|
async function getDataList() {
|
||||||
|
if (loading.value || finished.value) return;
|
||||||
|
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await request.get("/image/imageList", { ...listParams.value });
|
||||||
|
if (res.code === 200) {
|
||||||
|
// 如果是第一页,直接赋值,否则追加数据
|
||||||
|
if (listParams.value.pageNumber === 1) {
|
||||||
|
dataList.value = res.data.list;
|
||||||
|
} else {
|
||||||
|
dataList.value = [...dataList.value, ...res.data.list];
|
||||||
|
}
|
||||||
|
|
||||||
|
total.value = res.data.total; // 假设接口返回了总条数
|
||||||
|
|
||||||
|
// 判断是否加载完所有数据
|
||||||
|
if (dataList.value.length >= total.value) {
|
||||||
|
finished.value = true;
|
||||||
|
}
|
||||||
|
// 自动增加页码
|
||||||
|
listParams.value.pageNumber++;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
dataList.value = [];
|
||||||
|
finished.value = true;
|
||||||
|
console.log(err);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getDataList();
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener("scroll", topedRefresh);
|
||||||
|
observer.value = new IntersectionObserver(
|
||||||
|
([entry]) => {
|
||||||
|
if (entry.isIntersecting && !loading.value && !finished.value) {
|
||||||
|
getDataList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
threshold: 0.1,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (loadingTrigger.value) {
|
||||||
|
observer.value.observe(loadingTrigger.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener("scroll", topedRefresh);
|
||||||
|
if (observer.value) {
|
||||||
|
observer.value.disconnect();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function topedRefresh() {
|
||||||
|
if (import.meta.client) {
|
||||||
|
await nextTick();
|
||||||
|
window.scrollTo({
|
||||||
|
top: 0,
|
||||||
|
behavior: "smooth",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
initPageNUm();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onLike(item: any) {
|
||||||
|
try {
|
||||||
|
const res = await request.get(`/image/imageLike?id=${item.id}`);
|
||||||
|
if (res.code === 200) {
|
||||||
|
if (item.isLike === 1) {
|
||||||
|
item.isLike = 0;
|
||||||
|
item.likeNum -= 1;
|
||||||
|
message.success("取消点赞成功");
|
||||||
|
} else {
|
||||||
|
item.isLike = 1;
|
||||||
|
item.likeNum += 1;
|
||||||
|
message.success("点赞成功");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defineExpose({
|
||||||
|
initPageNUm,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
|
@ -1,15 +1,20 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { commonApi } from '@/api/common'
|
import { commonApi } from '@/api/common';
|
||||||
import { cloneDeep } from 'lodash-es'
|
import { cloneDeep } from 'lodash-es';
|
||||||
import { useMessage } from 'naive-ui'
|
import { useMessage } from 'naive-ui';
|
||||||
import { defineProps, ref } from 'vue'
|
import { defineProps, ref } from 'vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
formData: {
|
formData: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
|
type:{
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
debugger
|
||||||
const emit = defineEmits(['closePublishImg'])
|
const emit = defineEmits(['closePublishImg'])
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref } from 'vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
title: {
|
title: {
|
||||||
|
|
|
@ -0,0 +1,189 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-wrap justify-center">
|
||||||
|
<div class="grid grid-cols-2 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-7 gap-4 p-4">
|
||||||
|
<div
|
||||||
|
v-for="item in dataList"
|
||||||
|
:key="item.id"
|
||||||
|
class="relative rounded-lg overflow-hidden"
|
||||||
|
>
|
||||||
|
<!-- 图片 -->
|
||||||
|
<div class="relative h-[300px] overflow-hidden rounded-lg" @click="toDetail(item)">
|
||||||
|
<img
|
||||||
|
:src="item.coverPath"
|
||||||
|
class="w-full h-full object-cover rounded-lg cursor-pointer ransform transition-transform duration-300 hover:scale-110"
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 左上角标签 -->
|
||||||
|
<div
|
||||||
|
class="absolute top-2 left-2 bg-black/50 text-white text-xs px-2 py-1 rounded"
|
||||||
|
>
|
||||||
|
{{ item.type }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 底部数据统计 -->
|
||||||
|
<div
|
||||||
|
class="absolute bottom-0 left-0 right-0 flex items-center gap-2 p-2 text-xs text-white"
|
||||||
|
>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<component :is="Play" class="h-[14px] w-[14px] text-white menu-icon m-1" />
|
||||||
|
{{ item.useNumber || 0 }}
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<component
|
||||||
|
:is="Download"
|
||||||
|
class="h-[14px] w-[14px] text-white menu-icon m-1"
|
||||||
|
/>
|
||||||
|
{{ item.downloadNumber || 0 }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 作者信息条 -->
|
||||||
|
<div class="mt-1 px-2 py-1">
|
||||||
|
<div>
|
||||||
|
{{ item.workflowNam }}
|
||||||
|
</div>
|
||||||
|
<div class="flex mt-2">
|
||||||
|
<img :src="item.avatar" class="w-5 h-5 rounded-full mr-2" alt="" />
|
||||||
|
<span class="text-sm text-gray-500 truncate">{{ item.nickName }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
ref="loadingTrigger"
|
||||||
|
class="h-20 w-[1000px] text-center text-gray-500 flex justify-center items-center"
|
||||||
|
>
|
||||||
|
<div v-if="loading">加载中...</div>
|
||||||
|
<div v-if="finished && dataList.length >= 20">没有更多数据了</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Download, Play } from "lucide-vue-next";
|
||||||
|
import { nextTick, onMounted, onUnmounted, ref } from "vue";
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
const router = useRouter()
|
||||||
|
const loading = ref(false);
|
||||||
|
const finished = ref(false);
|
||||||
|
const total = ref(0); // 总条数
|
||||||
|
const loadingTrigger = ref(null);
|
||||||
|
const observer = ref<IntersectionObserver | null>(null);
|
||||||
|
const props = defineProps({
|
||||||
|
params: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const listParams = ref({
|
||||||
|
...props.params,
|
||||||
|
pageNumber: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
function initPageNUm() {
|
||||||
|
listParams.value.pageNumber = 1;
|
||||||
|
finished.value = false; // 重置加载完成状态
|
||||||
|
listParams.value = Object.assign({}, listParams.value, props.params);
|
||||||
|
getDataList();
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataList = ref([]);
|
||||||
|
async function getDataList() {
|
||||||
|
if (loading.value || finished.value) return;
|
||||||
|
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await request.post("/WorkFlow/workFlowList", { ...listParams.value });
|
||||||
|
if (res.code === 200) {
|
||||||
|
// 如果是第一页,直接赋值,否则追加数据
|
||||||
|
if (listParams.value.pageNumber === 1) {
|
||||||
|
dataList.value = res.data.list;
|
||||||
|
} else {
|
||||||
|
dataList.value = [...dataList.value, ...res.data.list];
|
||||||
|
}
|
||||||
|
|
||||||
|
total.value = res.data.total; // 假设接口返回了总条数
|
||||||
|
|
||||||
|
// 判断是否加载完所有数据
|
||||||
|
if (dataList.value.length >= total.value) {
|
||||||
|
finished.value = true;
|
||||||
|
}
|
||||||
|
// 自动增加页码
|
||||||
|
listParams.value.pageNumber++;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
dataList.value = [];
|
||||||
|
finished.value = true;
|
||||||
|
console.log(err);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getDataList();
|
||||||
|
|
||||||
|
// 跳转详情
|
||||||
|
function toDetail(item:any){
|
||||||
|
router.push(`/workflow-details/${item.id}`)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener("scroll", topedRefresh);
|
||||||
|
observer.value = new IntersectionObserver(
|
||||||
|
([entry]) => {
|
||||||
|
if (entry.isIntersecting && !loading.value && !finished.value) {
|
||||||
|
getDataList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
threshold: 0.1,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (loadingTrigger.value) {
|
||||||
|
observer.value.observe(loadingTrigger.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener("scroll", topedRefresh);
|
||||||
|
if (observer.value) {
|
||||||
|
observer.value.disconnect();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function topedRefresh() {
|
||||||
|
if (import.meta.client) {
|
||||||
|
await nextTick();
|
||||||
|
window.scrollTo({
|
||||||
|
top: 0,
|
||||||
|
behavior: "smooth",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
initPageNUm();
|
||||||
|
}
|
||||||
|
|
||||||
|
const workFlowCategoryList = ref([]);
|
||||||
|
async function getDictType() {
|
||||||
|
try {
|
||||||
|
const res = await commonApi.dictType({ type: "work_flow_type_child" });
|
||||||
|
if (res.code === 200) {
|
||||||
|
workFlowCategoryList.value = res.data;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getDictType();
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
initPageNUm,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
|
@ -0,0 +1,48 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in dataList"
|
||||||
|
:key="index"
|
||||||
|
@click="toDetail(item)"
|
||||||
|
class="bg-white h-20 rounded-lg p-3 mb-2 flex items-center justify-center cursor-pointer relative"
|
||||||
|
>
|
||||||
|
<div class="flex-1">
|
||||||
|
<div class="text-base ellipsis w-[650px]">{{ item.content }}</div>
|
||||||
|
<div class="text-[12px] text-gray-400 mt-1">{{ item.createTime }}</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="item.isRead === 0"
|
||||||
|
class="w-2 h-2 rounded-full bg-[#ec7768] absolute top-2 right-2"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<n-empty v-if="dataList.length === 0" size="large" description="暂无数据!">
|
||||||
|
</n-empty>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const props = defineProps({
|
||||||
|
dataList: {
|
||||||
|
type: Object,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
async function toDetail(item){
|
||||||
|
try{
|
||||||
|
const res = await request.get(`/advice/read?adviceId=${item.id}`)
|
||||||
|
debugger
|
||||||
|
|
||||||
|
}catch(err){
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.ellipsis {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,40 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in dataList"
|
||||||
|
:key="index"
|
||||||
|
class="bg-white h-20 rounded-lg p-3 mb-2 flex items-center justify-center cursor-pointer relative"
|
||||||
|
>
|
||||||
|
<div class="flex-1">
|
||||||
|
<div class="text-base ellipsis w-[600px]">{{ item.content }}</div>
|
||||||
|
<div class="text-[12px] text-gray-400 mt-1">{{ item.createTime }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="w-10 h-10">
|
||||||
|
<img class="w-full h-full rounded-lg" :src="item.userAvatar" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="item.isRead === 0"
|
||||||
|
class="w-2 h-2 rounded-full bg-[#ec7768] absolute top-2 right-2"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<n-empty v-if="dataList.length === 0" size="large" description="暂无数据!">
|
||||||
|
</n-empty>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const props = defineProps({
|
||||||
|
dataList: {
|
||||||
|
type: Object,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.ellipsis {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,19 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
v-for="item in 15"
|
||||||
|
class="bg-white h-20 rounded-lg p-3 mb-2 flex flex-col justify-center cursor-pointer"
|
||||||
|
>
|
||||||
|
<div class="text-base">你师傅说房东说短发女生的方式对佛呢</div>
|
||||||
|
<div class="text-[12px] text-gray-400 mt-1">2010-22-12 18:32:11</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,62 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in dataList" :key="index"
|
||||||
|
@click="toDetail(item)"
|
||||||
|
class="bg-white h-20 rounded-lg p-3 mb-2 flex items-center justify-center cursor-pointer relative"
|
||||||
|
>
|
||||||
|
<div class="flex-1">
|
||||||
|
<div class="text-base ellipsis w-[600px]">{{ item.content }}</div>
|
||||||
|
<div class="text-[12px] text-gray-400 mt-1">{{ item.createTime }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="w-10 h-10 ">
|
||||||
|
<img class="w-full h-full rounded-lg" :src="item.userAvatar" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="item.isRead === 0"
|
||||||
|
class="w-2 h-2 rounded-full bg-[#ec7768] absolute top-2 right-2"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<n-empty v-if="dataList.length === 0" size="large" description="暂无数据!">
|
||||||
|
</n-empty>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
dataList: {
|
||||||
|
type: Object,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
async function toDetail(item){
|
||||||
|
try{
|
||||||
|
debugger
|
||||||
|
const res = await request.get(`/advice/read?adviceId=${item.id}`)
|
||||||
|
if(res.code === 200){
|
||||||
|
// 0模型 1工作流 2图片
|
||||||
|
if(item.productType === 0){
|
||||||
|
router.push(`/model-details/${item.id}`)
|
||||||
|
}else if(item.productType === 1){
|
||||||
|
router.push(`/workflow-details/${item.id}`)
|
||||||
|
}else{
|
||||||
|
// onEditPicture()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch(err){
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.ellipsis{
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,7 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { FormInst } from 'naive-ui'
|
import { commonApi } from '@/api/common';
|
||||||
import { commonApi } from '@/api/common'
|
import type { FormInst } from 'naive-ui';
|
||||||
import { computed, watch } from 'vue'
|
import { computed, watch } from 'vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
|
@ -29,23 +29,17 @@ watch(
|
||||||
},
|
},
|
||||||
{ immediate: true, deep: true },
|
{ immediate: true, deep: true },
|
||||||
)
|
)
|
||||||
// 处理用户输入
|
|
||||||
function handleUpdate(value: Array<string>) {
|
|
||||||
if (value.length > 3) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
localForm.value.modelProduct.tags = value.join(',')
|
|
||||||
}
|
|
||||||
const formRef = ref<FormInst | null>(null)
|
const formRef = ref<FormInst | null>(null)
|
||||||
const rules = {
|
const rules = {
|
||||||
'modelProduct.modelName': {
|
'modelProduct.modelName': {
|
||||||
required: true,
|
required: true,
|
||||||
message: '请输入模型名称',
|
message: '',
|
||||||
trigger: 'blur',
|
trigger: 'blur',
|
||||||
},
|
},
|
||||||
'modelProduct.modelType': {
|
'modelProduct.modelType': {
|
||||||
required: true,
|
required: true,
|
||||||
message: '请输入模型名称',
|
message: '',
|
||||||
trigger: 'blur',
|
trigger: 'blur',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -103,11 +97,11 @@ getActivityList()
|
||||||
const originalBtnList = ref([
|
const originalBtnList = ref([
|
||||||
{
|
{
|
||||||
label: '原创',
|
label: '原创',
|
||||||
value: 0,
|
value: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '转载',
|
label: '转载',
|
||||||
value: 1,
|
value: 0,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
function nextStep() {
|
function nextStep() {
|
||||||
|
@ -122,67 +116,80 @@ function nextStep() {
|
||||||
}
|
}
|
||||||
function handleIsOriginal(value: number) {
|
function handleIsOriginal(value: number) {
|
||||||
localForm.value.modelProduct.isOriginal = value
|
localForm.value.modelProduct.isOriginal = value
|
||||||
if (value === 0) {
|
// if (value === 0) {
|
||||||
localForm.value.modelProduct.originalAuthorName = ''
|
// localForm.value.modelProduct.originalAuthorName = ''
|
||||||
}
|
// }
|
||||||
|
}
|
||||||
|
function handleCategoryUpdateValue(value) {
|
||||||
|
// 如果需要,可以在这里处理值的格式
|
||||||
|
// if (value) {
|
||||||
|
// // 确保值的格式正确
|
||||||
|
// localForm.value.modelProduct.category = value.includes('-') ? value : `${value}-0`
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
function changeModelType(item){
|
||||||
|
debugger
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<n-form
|
<div class="bg-gray-100 p-4 rounded-lg">
|
||||||
ref="formRef"
|
<n-form
|
||||||
:label-width="80"
|
ref="formRef"
|
||||||
:model="localForm"
|
:label-width="80"
|
||||||
:rules="rules"
|
:model="localForm"
|
||||||
size="large"
|
:rules="rules"
|
||||||
>
|
size="large"
|
||||||
<n-form-item label="模型名称" path="modelProduct.modelName">
|
>
|
||||||
<n-input v-model:value="localForm.modelProduct.modelName" placeholder="输入模型名" />
|
<n-form-item label="模型名称" path="modelProduct.modelName">
|
||||||
</n-form-item>
|
<n-input v-model:value="localForm.modelProduct.modelName" placeholder="输入模型名" />
|
||||||
<n-form-item label="模型类型" path="modelProduct.modelType">
|
</n-form-item>
|
||||||
<n-select
|
<n-form-item label="模型类型" path="modelProduct.modelType">
|
||||||
v-model:value="localForm.modelProduct.modelType"
|
<n-select
|
||||||
label-field="dictLabel"
|
v-model:value="localForm.modelProduct.modelType"
|
||||||
value-field="dictValue"
|
label-field="dictLabel"
|
||||||
placeholder="请选择模型类型"
|
@update:value="changeModelType"
|
||||||
:options="model_category"
|
value-field="dictValue"
|
||||||
/>
|
placeholder="请选择模型类型"
|
||||||
</n-form-item>
|
:options="model_category"
|
||||||
<div>
|
/>
|
||||||
内容类别
|
</n-form-item>
|
||||||
</div>
|
<div>
|
||||||
<div class="-mb-5 text-gray-400 text-[12px]">
|
内容类别
|
||||||
填写类别可让模型获得更精准的流量, 平台也有权基于标准修改你的类别标签
|
</div>
|
||||||
</div>
|
<div class="-mb-5 text-gray-400 text-[12px]">
|
||||||
<n-form-item path="category">
|
填写类别可让模型获得更精准的流量, 平台也有权基于标准修改你的类别标签
|
||||||
<n-cascader
|
</div>
|
||||||
v-model:value="localForm.modelProduct.category"
|
<n-form-item path="category">
|
||||||
placeholder="垂类"
|
<n-cascader
|
||||||
:options="categoryList"
|
v-model:value="localForm.modelProduct.category"
|
||||||
label-field="dictLabel"
|
placeholder="垂类"
|
||||||
value-field="dictValue"
|
:options="categoryList"
|
||||||
check-strategy="child"
|
label-field="dictLabel"
|
||||||
/>
|
value-field="dictValue"
|
||||||
</n-form-item>
|
check-strategy="child"
|
||||||
<n-form-item path="functions" class="-mt-12">
|
@update:value="handleCategoryUpdateValue"
|
||||||
<n-select
|
/>
|
||||||
v-model:value="localForm.modelProduct.functions"
|
</n-form-item>
|
||||||
label-field="dictLabel"
|
<n-form-item path="functions" class="-mt-12" v-if="localForm.modelProduct.modelType !== '0'">
|
||||||
value-field="dictValue"
|
<n-select
|
||||||
placeholder="功能"
|
v-model:value="localForm.modelProduct.functions"
|
||||||
:options="work_flow_functions"
|
label-field="dictLabel"
|
||||||
/>
|
value-field="dictValue"
|
||||||
</n-form-item>
|
placeholder="功能"
|
||||||
|
:options="work_flow_functions"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
标签
|
标签
|
||||||
</div>
|
</div>
|
||||||
<div class="-mb-5 text-gray-400 text-[12px]">
|
<div class="-mb-5 text-gray-400 text-[12px]">
|
||||||
添加标签将自动推荐给可能感兴趣的人
|
添加标签将自动推荐给可能感兴趣的人
|
||||||
</div>
|
</div>
|
||||||
<n-form-item path="category">
|
<n-form-item path="category">
|
||||||
<n-select
|
<!-- <n-select
|
||||||
v-model:value="localForm.modelProduct.tags"
|
v-model:value="localForm.modelProduct.tags"
|
||||||
:options="options"
|
:options="options"
|
||||||
filterable
|
filterable
|
||||||
|
@ -190,69 +197,85 @@ function handleIsOriginal(value: number) {
|
||||||
multiple
|
multiple
|
||||||
placeholder="请选择或输入标签"
|
placeholder="请选择或输入标签"
|
||||||
@update:value="handleUpdate"
|
@update:value="handleUpdate"
|
||||||
/>
|
/> -->
|
||||||
</n-form-item>
|
|
||||||
|
<n-select
|
||||||
|
v-model:value="localForm.modelProduct.tagsList"
|
||||||
|
filterable
|
||||||
|
multiple
|
||||||
|
tag
|
||||||
|
placeholder="输入,按回车确认"
|
||||||
|
:show-arrow="false"
|
||||||
|
:show="false"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
<div>
|
||||||
|
参与活动
|
||||||
|
</div>
|
||||||
|
<div class="-mb-5 text-gray-400 text-[12px]">
|
||||||
|
参与特定活动或比赛,下拉选择
|
||||||
|
</div>
|
||||||
|
<n-form-item path="activityId">
|
||||||
|
<n-select
|
||||||
|
v-model:value="localForm.modelProduct.activityId"
|
||||||
|
label-field="activityName"
|
||||||
|
value-field="id"
|
||||||
|
placeholder="请选择参与哪个活动"
|
||||||
|
:options="activityList"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
</n-form>
|
||||||
<div>
|
<div>
|
||||||
参与活动
|
原创内容
|
||||||
</div>
|
</div>
|
||||||
<div class="-mb-5 text-gray-400 text-[12px]">
|
<div class="flex justify-center items-center mt-5 bg-white h-12 rounded-lg border border-gray-100">
|
||||||
参与特定活动或比赛,下拉选择
|
<div
|
||||||
|
v-for="(item, index) in originalBtnList"
|
||||||
|
:key="index"
|
||||||
|
:style="{
|
||||||
|
backgroundColor:
|
||||||
|
localForm.modelProduct.isOriginal === item.value
|
||||||
|
? 'rgba(49, 98, 255, 0.1)'
|
||||||
|
: '#fff',
|
||||||
|
color: localForm.modelProduct.isOriginal === item.value ? '#3162ff' : '#000',
|
||||||
|
}" class="flex-1 rounded-lg h-full flex items-center justify-center cursor-pointer" @click="handleIsOriginal(item.value)"
|
||||||
|
>
|
||||||
|
{{ item.label }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<n-form-item path="activityId">
|
<div v-if="localForm.modelProduct.isOriginal === 1" class="text-[12px]">
|
||||||
<n-select
|
<div class="my-3">
|
||||||
v-model:value="localForm.modelProduct.activityId"
|
魔创未来原创模型加密保护计划
|
||||||
label-field="activityName"
|
</div>
|
||||||
value-field="id"
|
<div class="text-gray-400">
|
||||||
placeholder="请选择参与哪个活动"
|
原创模型加密保护计划是魔创未来推出的,为维护创作者权益、保障平台健康发展,通过技术手段遏制爬取、盗版、侵权等不法行为,保证创作者的劳动成果得到合理回报,进而激励更多优秀原创模型的诞生。
|
||||||
:options="activityList"
|
</div>
|
||||||
/>
|
<div class="text-gray-400">
|
||||||
</n-form-item>
|
谁能加入? 所有发布原创模型的作者,均可以加入本计划
|
||||||
</n-form>
|
</div>
|
||||||
<div class="flex justify-center items-center mt-5 bg-white h-10 rounded-lg">
|
<div class="my-3 text-gray-400">
|
||||||
<div
|
加入后有何好处? 1.模型加密能力 2.流量扶持 3.创作激励
|
||||||
v-for="(item, index) in originalBtnList"
|
</div>
|
||||||
:key="index"
|
<div class="text-gray-400">
|
||||||
:style="{
|
详情查看 <a href="" class="text-[#3162ff] underline">魔创未来原创模型加密保护计划</a>
|
||||||
backgroundColor:
|
</div>
|
||||||
localForm.modelProduct.isOriginal === item.value
|
<div class="my-3 ">
|
||||||
? 'rgba(49, 98, 255, 0.1)'
|
原创声明
|
||||||
: '#fff',
|
</div>
|
||||||
color: localForm.modelProduct.isOriginal === item.value ? '#3162ff' : '#000',
|
<div class="text-gray-400">
|
||||||
}" class="flex-1 rounded-lg h-full flex items-center justify-center cursor-pointer" @click="handleIsOriginal(item.value)"
|
本人声明并承诺模型是由本人原创,相关的权利和义务由本人承担。
|
||||||
>
|
</div>
|
||||||
{{ item.label }}
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<n-form-item path="modelProduct.originalAuthorName" size="large">
|
||||||
|
<n-input v-model:value="localForm.modelProduct.originalAuthorName" placeholder="输入原创作者" />
|
||||||
|
</n-form-item>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="localForm.modelProduct.isOriginal === 0" class="text-[12px]">
|
<div class="flex w-full justify-center">
|
||||||
<div class="my-3">
|
<div class="flex justify-center items-center mt-5 text-white w-[200px] h-10 rounded-lg bg-[#3162ff] cursor-pointer" @click="nextStep">
|
||||||
魔创未来原创模型加密保护计划
|
下一步
|
||||||
</div>
|
</div>
|
||||||
<div class="text-gray-400">
|
|
||||||
原创模型加密保护计划是魔创未来推出的,为维护创作者权益、保障平台健康发展,通过技术手段遏制爬取、盗版、侵权等不法行为,保证创作者的劳动成果得到合理回报,进而激励更多优秀原创模型的诞生。
|
|
||||||
</div>
|
|
||||||
<div class="text-gray-400">
|
|
||||||
谁能加入? 所有发布原创模型的作者,均可以加入本计划
|
|
||||||
</div>
|
|
||||||
<div class="my-3 text-gray-400">
|
|
||||||
加入后有何好处? 1.模型加密能力 2.流量扶持 3.创作激励
|
|
||||||
</div>
|
|
||||||
<div class="text-gray-400">
|
|
||||||
详情查看 <a href="" class="text-[#3162ff] underline">魔创未来原创模型加密保护计划</a>
|
|
||||||
</div>
|
|
||||||
<div class="my-3 ">
|
|
||||||
原创声明
|
|
||||||
</div>
|
|
||||||
<div class="text-gray-400">
|
|
||||||
本人声明并承诺模型是由本人原创,相关的权利和义务由本人承担。
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-else>
|
|
||||||
<n-form-item path="modelProduct.originalAuthorName" size="large">
|
|
||||||
<n-input v-model:value="localForm.modelProduct.originalAuthorName" placeholder="输入原创作者" />
|
|
||||||
</n-form-item>
|
|
||||||
</div>
|
|
||||||
<div class="flex justify-center items-center mt-5 text-white w-30 h-10 rounded-lg bg-[#3162ff] cursor-pointer" @click="nextStep">
|
|
||||||
下一步
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { FormInst } from 'naive-ui'
|
import { commonApi } from "@/api/common";
|
||||||
import { commonApi } from '@/api/common'
|
import { uploadImagesInBatches } from "@/utils/uploadImg.ts";
|
||||||
import { uploadImagesInBatches } from '@/utils/uploadImg.ts'
|
import { cloneDeep } from 'lodash-es';
|
||||||
import { Asterisk, Trash } from 'lucide-vue-next'
|
import { Asterisk, Trash } from "lucide-vue-next";
|
||||||
import { computed, onMounted, ref, watch } from 'vue'
|
import type { FormInst } from "naive-ui";
|
||||||
|
import { computed, ref, watch } from "vue";
|
||||||
|
const message = useMessage()
|
||||||
|
|
||||||
// 可接受的文件类型
|
// 可接受的文件类型
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
@ -11,321 +13,404 @@ const props = defineProps({
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
const emit = defineEmits(['update:modelValue', 'createModelsNext'])
|
const emit = defineEmits(["update:modelValue", "nextStep", "prevStep"]);
|
||||||
const acceptTypes = '.safetensors,.ckpt,.pt,.bin,.pth,.zip,.json,.flow,.lightflow,.yaml,.yml,.onnx,.gguf,.sft'
|
const acceptTypes =
|
||||||
|
".safetensors,.ckpt,.pt,.bin,.pth,.zip,.json,.flow,.lightflow,.yaml,.yml,.onnx,.gguf,.sft";
|
||||||
const modelVersionItem = {
|
const modelVersionItem = {
|
||||||
versionName: '', // 版本名称
|
delFlag: '0', // 0代表存在 2代表删除
|
||||||
modelId: null, // 基础模型
|
versionName: "", // 版本名称
|
||||||
versionDescription: '"<p>这是一个描述</p><p><img src=\"https://img1.baidu.com/it/u=3001150338,397170470&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1422\" /></p><p>这是两张图片之间的一些文字说明</p><p><img src=\"https://img12.iqilu.com/10339/clue/202405/29/68ec17f5-9621-461f-ad22-a6820a3f9cf5.jpg\" /></p>"', // 版本描述
|
modelVersionType: null, // 基础模型
|
||||||
filePath: '', // 文件路径
|
versionDescription: "", // 版本描述
|
||||||
fileName: '', // 文档里没有 ,表里没有
|
filePath: "", // 文件路径
|
||||||
sampleImagePaths: 'https://ybl2112.oss-cn-beijing.aliyuncs.com/2025/JANUARY/2/19/4/877e449c-3c0d-4630-a304-91ec110499f2.png,https://ybl2112.oss-cn-beijing.aliyuncs.com/2025/JANUARY/2/19/4/877e449c-3c0d-4630-a304-91ec110499f2.png,https://ybl2112.oss-cn-beijing.aliyuncs.com/2025/JANUARY/2/19/4/877e449c-3c0d-4630-a304-91ec110499f2.png', // 第三部的图片路径最多20张,切割
|
fileName: "", //
|
||||||
triggerWords: '', // 触发词
|
sampleImagePaths:[], // 第三部的图片路径最多20张,切割
|
||||||
isPublic: 1, // 权限是否公开权限 1公开 2自见
|
triggerWords: "", // 触发词
|
||||||
isFree: 0, // 是否免费 0免费 1会员 ????
|
isPublic: 1, // 权限是否公
|
||||||
isOnlineUse: 1, // 是否允许在线使用
|
isOnlineUse:1, //在线使用
|
||||||
allowDownloadImage: 1, // 允许下载生图
|
|
||||||
allowSoftwareUse: 1, // 允许在软件旗下使用
|
|
||||||
allowFusion: 1, // 是否允许融合
|
allowFusion: 1, // 是否允许融合
|
||||||
allowCommercialUse: 1, // 是否允许商用
|
isFree: 0, // 0免费
|
||||||
|
allowDownloadImage: 1, // 允许下载生图
|
||||||
|
allowSoftwareUse: 1, // 允许在软件旗下使用
|
||||||
|
allowCommercialUse: 1, // 是否允许商用
|
||||||
|
allowUsage: 1, // 允许模型转售或者融合出售
|
||||||
|
isExclusiveModel: 1, // 是否为独家模型这个字段
|
||||||
|
hideImageGenInfo:0, //隐藏图片生成信息
|
||||||
|
|
||||||
// allowUsage: 1, // 是否允许使用
|
};
|
||||||
// 允许模型转售或者融合手出售字段没找到?
|
|
||||||
isExclusiveModel: 0, // 是否为独家模型这个字段
|
|
||||||
}
|
|
||||||
const isPublicList = [
|
const isPublicList = [
|
||||||
{
|
{
|
||||||
label: '公开',
|
label: "公开",
|
||||||
value: 1,
|
value: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '仅自己可见',
|
label: "仅自己可见",
|
||||||
value: 2,
|
value: 0,
|
||||||
},
|
},
|
||||||
]
|
];
|
||||||
defineExpose({
|
defineExpose({
|
||||||
addVersion,
|
addVersion,
|
||||||
})
|
});
|
||||||
|
|
||||||
const localForm = computed({
|
const localForm = computed({
|
||||||
get() {
|
get() {
|
||||||
return props.modelValue
|
return props.modelValue;
|
||||||
},
|
},
|
||||||
set(value) {
|
set(value) {
|
||||||
emit('update:modelValue', value)
|
emit("update:modelValue", value);
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => localForm.value,
|
() => localForm.value,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
console.log('newVal', newVal)
|
emit("update:modelValue", newVal);
|
||||||
emit('update:modelValue', newVal)
|
|
||||||
},
|
},
|
||||||
{ immediate: true, deep: true },
|
{ immediate: true, deep: true }
|
||||||
)
|
);
|
||||||
|
|
||||||
const formRef = ref<FormInst | null>(null)
|
const formRefs = ref<(FormInst | null)[]>([]);
|
||||||
|
function setFormRef(el: FormInst | null, index: number) {
|
||||||
|
if (el) {
|
||||||
|
formRefs.value[index] = el;
|
||||||
|
}
|
||||||
|
}
|
||||||
const rules = {
|
const rules = {
|
||||||
versionName: {
|
versionName: {
|
||||||
required: true,
|
required: true,
|
||||||
message: '',
|
message: "",
|
||||||
trigger: 'blur',
|
trigger: "blur",
|
||||||
},
|
},
|
||||||
modelId: {
|
modelVersionType: {
|
||||||
required: true,
|
required: true,
|
||||||
message: '',
|
message: "",
|
||||||
trigger: 'blur',
|
trigger: "blur",
|
||||||
},
|
},
|
||||||
triggerWords: {
|
};
|
||||||
required: true,
|
|
||||||
message: '请输入模型名称',
|
|
||||||
trigger: 'blur',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
function addVersion() {
|
function addVersion() {
|
||||||
localForm.value.modelVersionList.unshift(modelVersionItem)
|
const param = cloneDeep(modelVersionItem)
|
||||||
|
localForm.value.modelVersionList.unshift(param);
|
||||||
}
|
}
|
||||||
const originalBtnList = ref([
|
const originalBtnList = ref([
|
||||||
{
|
{
|
||||||
label: '免费',
|
label: "免费",
|
||||||
value: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '会员下载',
|
|
||||||
value: 1,
|
value: 1,
|
||||||
},
|
},
|
||||||
])
|
{
|
||||||
|
label: "会员下载",
|
||||||
|
value: 0,
|
||||||
|
},
|
||||||
|
]);
|
||||||
function handleIsFree(index: number, value: number) {
|
function handleIsFree(index: number, value: number) {
|
||||||
localForm.value.modelVersionList[index].isFree = value
|
localForm.value.modelVersionList[index].isFree = value;
|
||||||
}
|
}
|
||||||
const model_category = ref([])
|
async function nextStep() {
|
||||||
function nextStep() {
|
for (let i = 0; i < localForm.value.modelVersionList.length; i++) {
|
||||||
formRef.value?.validate((errors) => {
|
if (
|
||||||
if (!errors) {
|
localForm.value.modelVersionList[i].delFlag === "0" &&
|
||||||
emit('createModelsNext')
|
localForm.value.modelVersionList[i].fileName === ""
|
||||||
|
) {
|
||||||
|
return message.error("请上传文件");
|
||||||
}
|
}
|
||||||
else {
|
const regex = /[\u4E00-\u9FA5]/; // 匹配汉字的正则表达式
|
||||||
console.log('error', errors)
|
if (
|
||||||
|
localForm.value.modelVersionList[i].delFlag === "0" &&
|
||||||
|
!regex.test(localForm.value.modelVersionList[i].versionDescription)
|
||||||
|
) {
|
||||||
|
return message.error("请用中文填写版本介绍");
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
try {
|
||||||
|
const promises = formRefs.value
|
||||||
|
.filter((form): form is FormInst => form !== null)
|
||||||
|
.map((form) => form.validate());
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
emit("nextStep");
|
||||||
|
} catch (errors) {
|
||||||
|
console.error("部分表单验证失败:", errors);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上传文件
|
// 上传文件
|
||||||
const uploadFileIndex = ref(0)
|
const uploadFileIndex = ref(0);
|
||||||
const fileInput = ref<HTMLInputElement | null>(null)
|
const fileInput = ref<HTMLInputElement | null>(null);
|
||||||
function triggerFileInput(index: number) {
|
function triggerFileInput(index: number) {
|
||||||
(fileInput.value as HTMLInputElement)?.click()
|
(fileInput.value as HTMLInputElement)?.click();
|
||||||
uploadFileIndex.value = index
|
uploadFileIndex.value = index;
|
||||||
}
|
}
|
||||||
async function handleFileChange(event: Event) {
|
async function handleFileChange(event: Event) {
|
||||||
const target = event.target as HTMLInputElement
|
const target = event.target as HTMLInputElement;
|
||||||
const files = target.files
|
const files = target.files;
|
||||||
|
|
||||||
if (files && files.length > 0) {
|
if (files && files.length > 0) {
|
||||||
const res = await uploadImagesInBatches(files)
|
const res = await uploadImagesInBatches(files);
|
||||||
localForm.value.modelVersionList[uploadFileIndex.value].filePath = res[0].url
|
localForm.value.modelVersionList[uploadFileIndex.value].filePath = res[0].url;
|
||||||
localForm.value.modelVersionList[uploadFileIndex.value].fileName = res[0].fileName
|
localForm.value.modelVersionList[uploadFileIndex.value].fileName = res[0].fileName;
|
||||||
}
|
}
|
||||||
target.value = ''
|
target.value = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function prevStep() {
|
||||||
|
emit("prevStep");
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseModelTypeList = ref([]);
|
||||||
|
async function getDictType() {
|
||||||
|
try {
|
||||||
|
const res = await commonApi.dictType({ type: "mode_type" });
|
||||||
|
if (res.code === 200) {
|
||||||
|
baseModelTypeList.value = res.data;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getDictType();
|
||||||
|
|
||||||
|
function computedDelFlag() {
|
||||||
|
return localForm.value.modelVersionList.filter(item => item.delFlag === '0')
|
||||||
|
}
|
||||||
|
function onDelete(index: number) {
|
||||||
|
if (computedDelFlag().length === 1)
|
||||||
|
return
|
||||||
|
localForm.value.modelVersionList[index].delFlag = '2'
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div v-for="(item, index) in localForm.modelVersionList" :key="index" class="bg-gray-100 p-4 rounded-lg">
|
<template
|
||||||
<n-form
|
v-for="(item, index) in localForm.modelVersionList"
|
||||||
ref="formRef"
|
:key="index"
|
||||||
:label-width="80"
|
class="bg-gray-100 p-4 mt-4 rounded-lg"
|
||||||
:model="localForm"
|
>
|
||||||
:rules="rules"
|
<div v-if="item.delFlag === '0'" class="bg-gray-100 p-4 rounded-lg mt-4 relative">
|
||||||
size="large"
|
<div class="absolute -right-10 top-4 cursor-pointer">
|
||||||
>
|
<Trash class="cursor-pointer" @click="onDelete(index)" />
|
||||||
<n-form-item label="版本名称" path="versionName">
|
|
||||||
<n-input v-model:value="item.versionName" placeholder="请输入版本名" />
|
|
||||||
</n-form-item>
|
|
||||||
<n-form-item label="基础模型" path="modelId">
|
|
||||||
<n-select
|
|
||||||
v-model:value="item.modelId"
|
|
||||||
label-field="dictLabel"
|
|
||||||
value-field="dictValue"
|
|
||||||
placeholder="请选择基础模型"
|
|
||||||
:options="model_category"
|
|
||||||
/>
|
|
||||||
</n-form-item>
|
|
||||||
<div class="flex">
|
|
||||||
上传文件 <Asterisk :size="10" color="#ff0000" class="mt-1" />
|
|
||||||
</div>
|
|
||||||
<div v-if="item.fileName" class="flex justify-between items-center bg-white p-3 mt-2 rounded-lg">
|
|
||||||
<div class="bg-[#d8e5fd] text-[12px] text-[#3162ff] w-16 h-7 rounded-lg flex justify-center items-center">
|
|
||||||
100%
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 flex items-center line-clamp">
|
<n-form
|
||||||
{{
|
:ref="(el) => setFormRef(el, index)"
|
||||||
item.fileName
|
:label-width="80"
|
||||||
}}
|
:model="localForm.modelVersionList[index]"
|
||||||
|
:rules="rules"
|
||||||
|
size="large"
|
||||||
|
>
|
||||||
|
<n-form-item label="版本名称" path="versionName">
|
||||||
|
<n-input v-model:value="item.versionName" placeholder="请输入版本名" />
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item label="基础模型" path="modelVersionType">
|
||||||
|
<n-select
|
||||||
|
v-model:value="item.modelVersionType"
|
||||||
|
label-field="dictLabel"
|
||||||
|
value-field="dictValue"
|
||||||
|
placeholder="请选择基础模型"
|
||||||
|
:options="baseModelTypeList"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
<div class="flex">
|
||||||
|
上传文件 <Asterisk :size="10" color="#ff0000" class="mt-1" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div
|
||||||
<Trash class="cursor-pointer" @click="item.fileName = '', item.filePath = ''" />
|
v-if="item.fileName"
|
||||||
|
class="flex justify-between items-center bg-white p-3 mt-2 rounded-lg"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="bg-[#d8e5fd] text-[12px] text-[#3162ff] w-16 h-7 rounded-lg flex justify-center items-center"
|
||||||
|
>
|
||||||
|
100%
|
||||||
|
</div>
|
||||||
|
<div class="flex-1 flex items-center line-clamp">
|
||||||
|
{{ item.fileName }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Trash
|
||||||
|
class="cursor-pointer"
|
||||||
|
@click="(item.fileName = ''), (item.filePath = '')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div v-else>
|
||||||
<div v-else>
|
<div class="upload-content">
|
||||||
<div class="upload-content">
|
<div
|
||||||
<div class="flex flex-col justify-center items-center w-30 h-40 border border-dashed mt-2 rounded-lg bg-white">
|
class="flex flex-col justify-center items-center w-30 h-40 border border-dashed mt-2 rounded-lg bg-white"
|
||||||
<div class="w-24 bg-gradient-to-r from-[#2D28FF] to-[#1A7DFF] h-8 text-white rounded-sm bg-[#3162ff] cursor-pointer flex justify-center items-center" @click="triggerFileInput(index)">
|
>
|
||||||
上传文件
|
<div
|
||||||
</div>
|
class="w-24 bg-gradient-to-r from-[#2D28FF] to-[#1A7DFF] h-8 text-white rounded-sm bg-[#3162ff] cursor-pointer flex justify-center items-center"
|
||||||
<div class="my-3">
|
@click="triggerFileInput(index)"
|
||||||
点击上传文件
|
>
|
||||||
</div>
|
上传文件
|
||||||
<div class="text-[#999999] text-xs">
|
</div>
|
||||||
.safetensors/.ckpt/.pt/.bin/.pth/.zip/.json/.flow/.lightflow/.yaml/.yml/.onnx/.gguf/.sft
|
<div class="my-3">点击上传文件</div>
|
||||||
|
<div class="text-[#999999] text-xs">
|
||||||
|
.safetensors/.ckpt/.pt/.bin/.pth/.zip/.json/.flow/.lightflow/.yaml/.yml/.onnx/.gguf/.sft
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="flex mt-6">
|
||||||
<div class="flex mt-6">
|
版本介绍 <Asterisk :size="10" color="#ff0000" class="mt-1" />
|
||||||
版本介绍 <Asterisk :size="10" color="#ff0000" class="mt-1" />
|
|
||||||
</div>
|
|
||||||
<div class="bg-white p-3 mt-2 rounded-lg">
|
|
||||||
<client-only>
|
|
||||||
<WangEditor v-model="item.versionDesc" />
|
|
||||||
</client-only>
|
|
||||||
</div>
|
|
||||||
<div v-html="item.versionDesc" />
|
|
||||||
<div class="mt-6">
|
|
||||||
触发词
|
|
||||||
</div>
|
|
||||||
<div class="-mb-5 text-gray-400 text-[12px]">
|
|
||||||
请输入您用来训练的单词
|
|
||||||
</div>
|
|
||||||
<n-form-item path="triggerWords">
|
|
||||||
<n-input v-model:value="item.triggerWords" placeholder="例如: 1boy" />
|
|
||||||
</n-form-item>
|
|
||||||
<div class="">
|
|
||||||
权限设置
|
|
||||||
</div>
|
|
||||||
<div class="mt-1 mb-2 text-gray-400 text-[12px]">
|
|
||||||
可见范围
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<n-radio-group v-model:value="item.isPublic" name="radiogroup">
|
|
||||||
<n-space>
|
|
||||||
<n-radio v-for="(isPublicItem, isPublicIndex) in isPublicList" :key="isPublicIndex" :value="isPublicItem.value">
|
|
||||||
{{ isPublicItem.label }}
|
|
||||||
</n-radio>
|
|
||||||
</n-space>
|
|
||||||
</n-radio-group>
|
|
||||||
</div>
|
|
||||||
</n-form>
|
|
||||||
<div v-if="item.isPublic === 1">
|
|
||||||
<div class="mt-4 mb-1 text-gray-400 text-[12px]">
|
|
||||||
付费设置
|
|
||||||
</div>
|
|
||||||
<div class="flex justify-center items-center bg-white h-10 rounded-lg">
|
|
||||||
<div
|
|
||||||
v-for="(subItem, subIndex) in originalBtnList"
|
|
||||||
:key="subIndex"
|
|
||||||
:style="{
|
|
||||||
backgroundColor:
|
|
||||||
item.isFree === subItem.value
|
|
||||||
? 'rgba(49, 98, 255, 0.1)'
|
|
||||||
: '#fff',
|
|
||||||
color: item.isFree === subItem.value ? '#3162ff' : '#000',
|
|
||||||
}" class="flex-1 rounded-lg h-full flex items-center justify-center cursor-pointer" @click="handleIsFree(index, subItem.value)"
|
|
||||||
>
|
|
||||||
{{ subItem.label }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="bg-white p-3 mt-2 rounded-lg">
|
||||||
<div class="mt-1 mb-2 text-gray-500 text-[12px]">
|
<client-only>
|
||||||
选择会员专属或会员下载视为您已经阅读 <span class="text-[#3162ff] cursor-pointer underline">《会员模型许可协议》</span> 并同意其中条款
|
<WangEditor v-model="item.versionDescription" />
|
||||||
</div>
|
</client-only>
|
||||||
<div v-if="item.isFree === 1" class="text-[12px]">
|
|
||||||
<div>会员下载模型</div>
|
|
||||||
<div class="text-gray-500">
|
|
||||||
下载模型需购买会员,在线生图对所有人开放,无生图次数限制;会员下载的模型版本生成图片默认可商用。
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="mt-6">触发词</div>
|
||||||
</div>
|
<div class="-mb-5 text-gray-400 text-[12px]">请输入您用来训练的单词</div>
|
||||||
<div class="text-gray-400 text-[12px] my-4">
|
<n-form-item path="triggerWords">
|
||||||
许可范围
|
<n-input v-model:value="item.triggerWords" placeholder="例如: 1boy" />
|
||||||
</div>
|
</n-form-item>
|
||||||
<div class="flex flex-wrap">
|
|
||||||
<div class="w-[50%] mb-2">
|
|
||||||
<n-checkbox
|
|
||||||
v-model:checked="item.isOnlineUse"
|
|
||||||
:checked-value="1"
|
|
||||||
:unchecked-value="0" label="允许在魔创未来在线使用"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="w-[50%] mb-2">
|
|
||||||
<n-checkbox
|
|
||||||
v-model:checked="item.allowDownloadImage"
|
|
||||||
:checked-value="1"
|
|
||||||
:unchecked-value="0" label="允许下载生图"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="w-[50%] mb-2">
|
|
||||||
<n-checkbox
|
|
||||||
v-model:checked="item.allowSoftwareUse"
|
|
||||||
:checked-value="1"
|
|
||||||
:unchecked-value="0" label="允许在魔创未来旗下其他产品在线使用"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="w-[50%] mb-2">
|
|
||||||
<n-checkbox
|
|
||||||
v-model:checked="item.allowFusion"
|
|
||||||
:checked-value="1"
|
|
||||||
:unchecked-value="0" label="允许进行融合"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="text-gray-400 text-[12px] my-4">
|
<div v-if="localForm.modelProduct.modelType === '0'">
|
||||||
商用许可范围
|
<n-form-item label="采样方法" path="modelVersionType">
|
||||||
</div>
|
<n-select
|
||||||
<div class="flex flex-wrap">
|
v-model:value="item.modelVersionType"
|
||||||
<div class="w-[50%] mb-2">
|
label-field="dictLabel"
|
||||||
<n-checkbox
|
value-field="dictValue"
|
||||||
v-model:checked="item.allowCommercialUse"
|
placeholder="请选择采样方法"
|
||||||
:checked-value="1"
|
:options="baseModelTypeList"
|
||||||
:unchecked-value="0" label="生成图片可出售或用于商业目的"
|
/>
|
||||||
/>
|
</n-form-item>
|
||||||
</div>
|
<div>
|
||||||
<div class="w-[50%] mb-2">
|
<n-select v-model:value="item.modelVersionType" multiple :options="baseModelTypeList" />
|
||||||
<n-checkbox
|
</div>
|
||||||
v-model:checked="item.allowDownloadImage"
|
</div>
|
||||||
:checked-value="1"
|
|
||||||
:unchecked-value="0" label="允许模型转售或融合后出售"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-4">
|
<div class="">权限设置</div>
|
||||||
<div class="text-gray-400 text-[12px] my-4">
|
<div class="mt-1 mb-2 text-gray-400 text-[12px]">可见范围</div>
|
||||||
独家设置
|
<div>
|
||||||
|
<n-radio-group v-model:value="item.isPublic" name="radiogroup">
|
||||||
|
<n-space>
|
||||||
|
<n-radio
|
||||||
|
v-for="(isPublicItem, isPublicIndex) in isPublicList"
|
||||||
|
:key="isPublicIndex"
|
||||||
|
:value="isPublicItem.value"
|
||||||
|
>
|
||||||
|
{{ isPublicItem.label }}
|
||||||
|
</n-radio>
|
||||||
|
</n-space>
|
||||||
|
</n-radio-group>
|
||||||
|
</div>
|
||||||
|
</n-form>
|
||||||
|
<div v-if="item.isPublic === 1">
|
||||||
|
<div class="mt-4 mb-1 text-gray-400 text-[12px]">付费设置</div>
|
||||||
|
<div class="flex justify-center items-center bg-white h-10 rounded-lg">
|
||||||
|
<div
|
||||||
|
v-for="(subItem, subIndex) in originalBtnList"
|
||||||
|
:key="subIndex"
|
||||||
|
:style="{
|
||||||
|
backgroundColor:
|
||||||
|
item.isFree === subItem.value ? 'rgba(49, 98, 255, 0.1)' : '#fff',
|
||||||
|
color: item.isFree === subItem.value ? '#3162ff' : '#000',
|
||||||
|
}"
|
||||||
|
class="flex-1 rounded-lg h-full flex items-center justify-center cursor-pointer"
|
||||||
|
@click="handleIsFree(index, subItem.value)"
|
||||||
|
>
|
||||||
|
{{ subItem.label }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-1 mb-2 text-gray-500 text-[12px]">
|
||||||
|
选择会员专属或会员下载视为您已经阅读
|
||||||
|
<span class="text-[#3162ff] cursor-pointer underline"
|
||||||
|
>《会员模型许可协议》</span
|
||||||
|
>
|
||||||
|
并同意其中条款
|
||||||
|
</div>
|
||||||
|
<div v-if="item.isFree === 1" class="text-[12px]">
|
||||||
|
<div>会员下载模型</div>
|
||||||
|
<div class="text-gray-500">
|
||||||
|
下载模型需购买会员,在线生图对所有人开放,无生图次数限制;会员下载的模型版本生成图片默认可商用。
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center mb-2">
|
<div class="text-gray-400 text-[12px] my-4">许可范围</div>
|
||||||
<n-checkbox
|
<div class="flex flex-wrap">
|
||||||
v-model:checked="item.isExclusiveModel"
|
<div class="w-[50%] mb-2">
|
||||||
:checked-value="1"
|
<n-checkbox
|
||||||
:unchecked-value="0"
|
v-model:checked="item.isOnlineUse"
|
||||||
/>
|
:checked-value="1"
|
||||||
<div class="ml-3 text-[12px] text-gray-500">
|
:unchecked-value="0"
|
||||||
此版本为魔创未来独家模型 *获取更多流量:独家模型规则
|
disabled="true"
|
||||||
|
label="允许在魔创未来在线使用"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="w-[50%] mb-2">
|
||||||
|
<n-checkbox
|
||||||
|
v-model:checked="item.allowDownloadImage"
|
||||||
|
:checked-value="1"
|
||||||
|
:unchecked-value="0"
|
||||||
|
label="允许下载生图"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="w-[50%] mb-2">
|
||||||
|
<n-checkbox
|
||||||
|
v-model:checked="item.allowSoftwareUse"
|
||||||
|
:checked-value="1"
|
||||||
|
disabled="true"
|
||||||
|
:unchecked-value="0"
|
||||||
|
label="允许在魔创未来旗下其他产品在线使用"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="w-[50%] mb-2">
|
||||||
|
<n-checkbox
|
||||||
|
v-model:checked="item.allowFusion"
|
||||||
|
:checked-value="1"
|
||||||
|
:unchecked-value="0"
|
||||||
|
label="允许进行融合"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-gray-400 text-[12px] my-4">商用许可范围</div>
|
||||||
|
<div class="flex flex-wrap">
|
||||||
|
<div class="w-[50%] mb-2">
|
||||||
|
<n-checkbox
|
||||||
|
v-model:checked="item.allowCommercialUse"
|
||||||
|
:checked-value="1"
|
||||||
|
:unchecked-value="0"
|
||||||
|
label="生成图片可出售或用于商业目的"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="w-[50%] mb-2">
|
||||||
|
<n-checkbox
|
||||||
|
v-model:checked="item.allowUsage"
|
||||||
|
:checked-value="1"
|
||||||
|
:unchecked-value="0"
|
||||||
|
label="允许模型转售或融合后出售"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<div class="text-gray-400 text-[12px] my-4">独家设置</div>
|
||||||
|
<div class="flex items-center mb-2">
|
||||||
|
<n-checkbox
|
||||||
|
v-model:checked="item.isExclusiveModel"
|
||||||
|
:checked-value="1"
|
||||||
|
:unchecked-value="0"
|
||||||
|
/>
|
||||||
|
<div class="ml-3 text-[12px] text-gray-500">
|
||||||
|
此版本为魔创未来独家模型 *获取更多流量:独家模型规则
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
<div class="flex items-center justify-center mt-5">
|
<div class="flex items-center justify-center mt-5">
|
||||||
<div class="flex justify-center items-center mt-5 w-[20%] mx-2 h-10 rounded-lg bg-[#f1f2f7] cursor-pointer" @click="nextStep">
|
<div
|
||||||
上一步
|
class="flex justify-center items-center mt-5 w-[20%] mx-2 h-10 rounded-lg bg-[#f1f2f7] cursor-pointer"
|
||||||
|
@click="prevStep"
|
||||||
|
>
|
||||||
|
上一步
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="flex justify-center items-center mt-5 text-white mx-2 w-[20%] h-10 rounded-lg bg-[#3162ff] cursor-pointer"
|
||||||
|
@click="nextStep"
|
||||||
|
>
|
||||||
|
下一步
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-center items-center mt-5 text-white mx-2 w-[20%] h-10 rounded-lg bg-[#3162ff] cursor-pointer" @click="nextStep">
|
|
||||||
下一步
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
ref="fileInput"
|
ref="fileInput"
|
||||||
|
@ -333,7 +418,7 @@ async function handleFileChange(event: Event) {
|
||||||
class="hidden"
|
class="hidden"
|
||||||
:accept="acceptTypes"
|
:accept="acceptTypes"
|
||||||
@change="handleFileChange"
|
@change="handleFileChange"
|
||||||
>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -0,0 +1,197 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
import { Asterisk } from 'lucide-vue-next';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const emit = defineEmits(['update:modelValue', 'preStep'])
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const { type } = route.query
|
||||||
|
|
||||||
|
const message = useMessage()
|
||||||
|
|
||||||
|
const localForm = computed({
|
||||||
|
get() {
|
||||||
|
return props.modelValue
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
emit('update:modelValue', value)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
function preStep() {
|
||||||
|
emit('preStep')
|
||||||
|
}
|
||||||
|
const showSuccessModal = ref(false)
|
||||||
|
|
||||||
|
async function handlePublish() {
|
||||||
|
for (let i = 0; i < localForm.value.modelVersionList.length; i++) {
|
||||||
|
if (localForm.value.modelVersionList[i].delFlag === '0' && localForm.value.modelVersionList[i].sampleImagePaths.length === 0) {
|
||||||
|
return message.error('请上传图片')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const param = cloneDeep(localForm.value)
|
||||||
|
if (param.modelProduct.tagsList.length !== 0) {
|
||||||
|
param.modelProduct.tags = JSON.stringify(param.modelProduct.tagsList)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
param.modelProduct.type = '[]'
|
||||||
|
}
|
||||||
|
for (let i = 0; i < param.modelVersionList.length; i++) {
|
||||||
|
if (param.modelVersionList[i].sampleImagePaths.length !== 0) {
|
||||||
|
param.modelVersionList[i].sampleImagePaths = param.modelVersionList[i].sampleImagePaths.join(',')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
param.modelVersionList[i].sampleImagePaths = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'add') {
|
||||||
|
try {
|
||||||
|
const res = await request.post('/model/insert', param)
|
||||||
|
if (res.code === 200) {
|
||||||
|
showSuccessModal.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
try {
|
||||||
|
const res = await request.post('/model/update', param)
|
||||||
|
if (res.code === 200) {
|
||||||
|
showSuccessModal.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
watch(
|
||||||
|
() => localForm.value,
|
||||||
|
(newVal) => {
|
||||||
|
emit('update:modelValue', newVal)
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true },
|
||||||
|
)
|
||||||
|
const fileInput = ref<HTMLInputElement | null>(null)
|
||||||
|
const uploadFileIndex = ref(0)
|
||||||
|
|
||||||
|
function triggerFileInput(index: number) {
|
||||||
|
(fileInput.value as HTMLInputElement)?.click()
|
||||||
|
uploadFileIndex.value = index
|
||||||
|
}
|
||||||
|
async function handleFileChange(event: Event) {
|
||||||
|
const target = event.target as HTMLInputElement
|
||||||
|
const files = target.files
|
||||||
|
if (files && files.length > 0) {
|
||||||
|
const sum = localForm.value.modelVersionList[uploadFileIndex.value].sampleImagePaths.length + files.length
|
||||||
|
if (sum >= 20)
|
||||||
|
return message.error('最多20张')
|
||||||
|
const res = await uploadImagesInBatches(files)
|
||||||
|
const urlList = res.map(item => item.url)
|
||||||
|
localForm.value.modelVersionList[uploadFileIndex.value].sampleImagePaths.push(...urlList)
|
||||||
|
}
|
||||||
|
target.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
function onPositiveClick() {
|
||||||
|
showSuccessModal.value = false
|
||||||
|
router.push('/personal-center')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="mx-auto mt-10">
|
||||||
|
<div class="w-[700px] mx-auto items-start rounded-lg">
|
||||||
|
<template v-for="(item, index) in localForm.modelVersionList" :key="index">
|
||||||
|
<div v-if="item.delFlag === '0'" class="w-full bg-gray-100 p-4 rounded-lg mt-2">
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<div>
|
||||||
|
版本名: <span>{{ item.versionName }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<n-checkbox
|
||||||
|
v-model:checked="item.hideImageGenInfo"
|
||||||
|
:checked-value="1" :unchecked-value="0"
|
||||||
|
/>
|
||||||
|
隐藏图片生成信息
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between items-center mt-4">
|
||||||
|
<div class="flex">
|
||||||
|
添加版本示例图片
|
||||||
|
<Asterisk :size="10" color="#ff0000" class="mt-1" />
|
||||||
|
</div>
|
||||||
|
<div class="text-[12px] text-gray-400">
|
||||||
|
最多20张图片, 图片不超过30M
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="flex flex-col justify-center items-center w-30 h-40 border border-dashed mt-2 rounded-lg bg-white">
|
||||||
|
<div class="w-24 bg-gradient-to-r from-[#2D28FF] to-[#1A7DFF] h-8 text-white rounded-sm bg-[#3162ff] cursor-pointer flex justify-center items-center" @click="triggerFileInput(index)">
|
||||||
|
上传文件
|
||||||
|
</div>
|
||||||
|
<div class="my-3">
|
||||||
|
点击上传文件
|
||||||
|
</div>
|
||||||
|
<div class="text-[#999999] text-xs">
|
||||||
|
请勿上传裸露、暴力、血腥或其他包含非法信息图片
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-3 gap-2.5 mt-4 w-full">
|
||||||
|
<div v-for="(subItem, subIndex) in item.sampleImagePaths" :key="subIndex">
|
||||||
|
<img class="w-full h-[200px] object-cover rounded-lg" :src="subItem" alt="">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-center mt-5">
|
||||||
|
<div class="flex justify-center items-center mt-5 w-[20%] mx-2 h-10 rounded-lg bg-[#f1f2f7] cursor-pointer" @click="preStep">
|
||||||
|
上一步
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-center items-center mt-5 text-white mx-2 w-[20%] h-10 rounded-lg bg-[#3162ff] cursor-pointer" @click="handlePublish">
|
||||||
|
发布
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
ref="fileInput"
|
||||||
|
type="file"
|
||||||
|
class="hidden"
|
||||||
|
accept="image/*"
|
||||||
|
multiple
|
||||||
|
@change="handleFileChange"
|
||||||
|
>
|
||||||
|
|
||||||
|
<!-- 成功之后的弹框 -->
|
||||||
|
<n-modal
|
||||||
|
v-model:show="showSuccessModal"
|
||||||
|
:mask-closable="false"
|
||||||
|
preset="dialog"
|
||||||
|
title="发布成功!"
|
||||||
|
content="工作流发布成功, 可至个人中心查看状态"
|
||||||
|
positive-text="确认"
|
||||||
|
@positive-click="onPositiveClick"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -1,8 +1,9 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { FormInst } from 'naive-ui'
|
import { uploadFileBatches } from '@/utils/uploadImg.ts';
|
||||||
import { uploadFileBatches } from '@/utils/uploadImg.ts'
|
import { cloneDeep } from 'lodash-es';
|
||||||
import { Asterisk, Trash } from 'lucide-vue-next'
|
import { Asterisk, Trash } from 'lucide-vue-next';
|
||||||
import { computed, ref, watch } from 'vue'
|
import type { FormInst } from 'naive-ui';
|
||||||
|
import { computed, ref, watch } from 'vue';
|
||||||
|
|
||||||
// 可接受的文件类型
|
// 可接受的文件类型
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
@ -54,7 +55,8 @@ const rules = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
function addVersion() {
|
function addVersion() {
|
||||||
localForm.value.workFlowVersionList.unshift(modelVersionItem)
|
const param = cloneDeep(modelVersionItem)
|
||||||
|
localForm.value.workFlowVersionList.unshift(param)
|
||||||
}
|
}
|
||||||
|
|
||||||
const formRefs = ref<(FormInst | null)[]>([])
|
const formRefs = ref<(FormInst | null)[]>([])
|
||||||
|
|
|
@ -70,9 +70,9 @@ function handleIsPublic(value: number) {
|
||||||
}
|
}
|
||||||
function handleIsOriginal(value: number) {
|
function handleIsOriginal(value: number) {
|
||||||
localForm.value.workFlow.original = value
|
localForm.value.workFlow.original = value
|
||||||
if (value === 0) {
|
// if (value === 0) {
|
||||||
localForm.value.workFlow.authorName = ''
|
// localForm.value.workFlow.authorName = ''
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 内容类别
|
// 内容类别
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { cloneDeep } from 'lodash-es'
|
import { cloneDeep } from 'lodash-es';
|
||||||
import { Asterisk } from 'lucide-vue-next'
|
import { Asterisk } from 'lucide-vue-next';
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
|
@ -53,6 +53,9 @@ async function handlePublish() {
|
||||||
param.workFlowVersionList[i].imagePaths = ''
|
param.workFlowVersionList[i].imagePaths = ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(param.modelProduct.workFlow === 0){
|
||||||
|
param.modelProduct.originalAuthorName = ''
|
||||||
|
}
|
||||||
// await request.post('/WorkFlow/addWorkFlow', param)
|
// await request.post('/WorkFlow/addWorkFlow', param)
|
||||||
if (type === 'add') {
|
if (type === 'add') {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import type { SearchCheckIcon } from 'lucide-vue-next'
|
|
||||||
|
|
||||||
export const appName = '魔创未来'
|
export const appName = '魔创未来'
|
||||||
export const appDescription = '魔创未来'
|
export const appDescription = '魔创未来'
|
||||||
|
@ -19,5 +18,5 @@ export const isPublicList = [{
|
||||||
value: 2,
|
value: 2,
|
||||||
}]
|
}]
|
||||||
export const headerRole = { // 包括就隐藏
|
export const headerRole = { // 包括就隐藏
|
||||||
inputSearch: ['/model-square'],
|
inputSearch: ['/model-square','/picture-square', '/work-square'],
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {
|
import {
|
||||||
Binary,
|
Binary,
|
||||||
Code2,
|
Code2,
|
||||||
Crown,
|
Crown,
|
||||||
Image,
|
Image,
|
||||||
LayoutGrid,
|
LayoutGrid,
|
||||||
Lightbulb,
|
Lightbulb,
|
||||||
Maximize,
|
Maximize,
|
||||||
User,
|
User,
|
||||||
Workflow,
|
Workflow
|
||||||
} from 'lucide-vue-next'
|
} from 'lucide-vue-next';
|
||||||
|
|
||||||
const menuStore = useMenuStore()
|
const menuStore = useMenuStore()
|
||||||
|
|
||||||
// 路径到图标的映射
|
// 路径到图标的映射
|
||||||
const iconMap: any = {
|
const iconMap: any = {
|
||||||
'/model-square': LayoutGrid,
|
'/model-square': LayoutGrid,
|
||||||
'/works-inspire': Lightbulb,
|
'/picture-square':Lightbulb,
|
||||||
'/workspace': Lightbulb,
|
'/work-square':Workflow,
|
||||||
'/web-ui': Image,
|
'/web-ui': Image,
|
||||||
'/comfy-ui': Workflow,
|
'/comfy-ui': Workflow,
|
||||||
'/training-lora': Binary,
|
'/training-lora': Binary,
|
||||||
|
@ -78,7 +78,7 @@ function handleSide(event: Event, path: string) {
|
||||||
v-for="item in menuStore.menuItems"
|
v-for="item in menuStore.menuItems"
|
||||||
:key="item.path"
|
:key="item.path"
|
||||||
:to="item.path"
|
:to="item.path"
|
||||||
class="flex items-center gap-3 rounded-lg px-4 py-2.5 text-[15px] font-medium no-underline transition-colors"
|
class="flex items-center gap-3 rounded-lg px-4 py-4 text-[15px] font-medium no-underline transition-colors"
|
||||||
:class="[
|
:class="[
|
||||||
menuStore.activeMenu === item.path
|
menuStore.activeMenu === item.path
|
||||||
? 'bg-blue-500/8 text-blue-600 dark:bg-blue-500/10 dark:text-blue-400'
|
? 'bg-blue-500/8 text-blue-600 dark:bg-blue-500/10 dark:text-blue-400'
|
||||||
|
|
|
@ -0,0 +1,272 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import AttentionMsg from "@/components/message/AttentionMsg.vue";
|
||||||
|
import LikeMsg from "@/components/message/LikeMsg.vue";
|
||||||
|
import OfficialMsg from "@/components/message/OfficialMsg.vue";
|
||||||
|
import ReplyMsg from "@/components/message/ReplyMsg.vue";
|
||||||
|
import { Check, Heart, MessageSquareMore, UserPlus, Volume2 } from "lucide-vue-next";
|
||||||
|
|
||||||
|
const currentMsgType = ref("reply");
|
||||||
|
const MsgTypeList = ref([
|
||||||
|
// {
|
||||||
|
// label:'官方通知',
|
||||||
|
// value:'official'
|
||||||
|
// icon:'Volume2'
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
label: "回复我的",
|
||||||
|
value: "reply",
|
||||||
|
icon: "MessageSquareMore",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "收到的赞",
|
||||||
|
value: "like",
|
||||||
|
icon: "Heart",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "关注我的",
|
||||||
|
value: "attention",
|
||||||
|
icon: "UserPlus",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const iconMap: any = {
|
||||||
|
'official':Volume2,
|
||||||
|
'reply': MessageSquareMore,
|
||||||
|
'like':Heart,
|
||||||
|
'attention':UserPlus,
|
||||||
|
}
|
||||||
|
|
||||||
|
MsgTypeList.value = MsgTypeList.value.map(item => ({
|
||||||
|
...item,
|
||||||
|
LucideIcon: iconMap[item.value], // 添加 Lucide 图标组件
|
||||||
|
}))
|
||||||
|
|
||||||
|
const urlList = ref({
|
||||||
|
official: "",
|
||||||
|
reply: "/advice/getCommentMsg",
|
||||||
|
like: "/advice/getLikeMsg",
|
||||||
|
attention: "/advice/getAttentionMsg",
|
||||||
|
});
|
||||||
|
const allList = ref({
|
||||||
|
reply: [],
|
||||||
|
like: [],
|
||||||
|
attention: [],
|
||||||
|
});
|
||||||
|
// const officialList = ref([])
|
||||||
|
// const replyList = ref([])
|
||||||
|
// const likeList = ref([])
|
||||||
|
// const attentionList = ref([])
|
||||||
|
|
||||||
|
// const officialParamsType = ref('')
|
||||||
|
// const officialParamsTypeList = ref([
|
||||||
|
// {
|
||||||
|
// label:'全部通知',
|
||||||
|
// value:'4'
|
||||||
|
// }
|
||||||
|
// ])
|
||||||
|
const replyParamsType = ref("3");
|
||||||
|
const replyParamsTypeList = ref([
|
||||||
|
{
|
||||||
|
label: "全部通知",
|
||||||
|
value: "3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "模型评论",
|
||||||
|
value: "0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "图片评论",
|
||||||
|
value: "2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "工作流评论",
|
||||||
|
value: "1",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const likeParamsType = ref("4");
|
||||||
|
const likeParamsTypeList = ref([
|
||||||
|
{
|
||||||
|
label: "全部通知",
|
||||||
|
value: "4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "模型点赞",
|
||||||
|
value: "0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "图片点赞",
|
||||||
|
value: "2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "工作流点赞",
|
||||||
|
value: "1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "评论点赞",
|
||||||
|
value: "3",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
async function getList() {
|
||||||
|
const url = urlList.value[currentMsgType.value];
|
||||||
|
let productType = "";
|
||||||
|
if (currentMsgType.value === "reply") {
|
||||||
|
productType = replyParamsType.value;
|
||||||
|
} else if (currentMsgType.value === "like") {
|
||||||
|
productType = likeParamsType.value;
|
||||||
|
} else {
|
||||||
|
productType = "";
|
||||||
|
}
|
||||||
|
const res = await request.get(url, { productType });
|
||||||
|
if (res.code === 200) {
|
||||||
|
allList.value[currentMsgType.value] = res.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getList();
|
||||||
|
|
||||||
|
function changeType(item: any) {
|
||||||
|
currentMsgType.value = item.value;
|
||||||
|
likeParamsType.value = "4";
|
||||||
|
replyParamsType.value = "3";
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
function handleSelect(item: any, type: string) {
|
||||||
|
if (type === "like") {
|
||||||
|
likeParamsType.value = item.value;
|
||||||
|
} else {
|
||||||
|
replyParamsType.value = item.value;
|
||||||
|
}
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
// 一键已读
|
||||||
|
async function onAllRead() {
|
||||||
|
try {
|
||||||
|
const res = await request.get("/advice/readAll");
|
||||||
|
if (res.code === 200) {
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
definePageMeta({
|
||||||
|
layout: "default",
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="bg-[#f0f1f5] w-full flex items-center justify-center"
|
||||||
|
:style="{ height: `calc(100vh - 48px)` }"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="card w-[1036px] min-h-[500px] m-auto rounded-lg flex border border-solid border-gray-200"
|
||||||
|
:style="{ height: `calc(100vh - 100px)` }"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="w-[287px] bg-white p-3 box-content rounded-l-lg text-gray-600 flex flex-col justify-between"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div class="p-3 text-xl">通知</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="msg-item"
|
||||||
|
:style="{ background: item.value === currentMsgType ? '#f5f6fa' : '' }"
|
||||||
|
v-for="(item, index) in MsgTypeList"
|
||||||
|
:key="index"
|
||||||
|
@click="changeType(item)"
|
||||||
|
>
|
||||||
|
<!-- <Volume2 size="18" /> -->
|
||||||
|
<!-- <MessageSquareMore size="18" /> -->
|
||||||
|
<!-- <Heart size="18" /> -->
|
||||||
|
<!-- <UserPlus
|
||||||
|
size="18"
|
||||||
|
:style="{ color: item.value === currentMsgType ? '#3541ec' : '' }"
|
||||||
|
class="mr-2"
|
||||||
|
/> -->
|
||||||
|
|
||||||
|
<component
|
||||||
|
:is="item.LucideIcon"
|
||||||
|
size="18"
|
||||||
|
:style="{ color: item.value === currentMsgType ? '#3541ec' : '' }"
|
||||||
|
class="mr-2"
|
||||||
|
/>
|
||||||
|
{{ item.label }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="w-full flex py-2 cursor-pointer justify-center">
|
||||||
|
<div class="flex justify-center items-center cursor-pointer" @click="onAllRead">
|
||||||
|
<Check size="14" class="mr-1" />
|
||||||
|
一键已读
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box-content p-3 flex-1">
|
||||||
|
<div class="w-full h-10 flex justify-end items-center cursor-pointer relative">
|
||||||
|
<div class="group py-2">
|
||||||
|
<template v-if="currentMsgType === 'reply' || currentMsgType === 'like'">
|
||||||
|
全部通知
|
||||||
|
<n-icon size="14" class="ml-1">
|
||||||
|
<ChevronDown />
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="currentMsgType === 'reply'"
|
||||||
|
class="absolute right-0 top-[30px] hidden group-hover:block text-[#000000] text-[12px] bg-white rounded-lg text-center px-2 py-2 w-20 mt-2 shadow-lg z-10"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in replyParamsTypeList"
|
||||||
|
:style="{
|
||||||
|
color: item.value === replyParamsType ? '#3a75f6' : '',
|
||||||
|
}"
|
||||||
|
:key="index"
|
||||||
|
class="hover:bg-gray-100 py-2 cursor-pointer rounded-lg"
|
||||||
|
@click="(event) => handleSelect(item, 'reply')"
|
||||||
|
>
|
||||||
|
{{ item.label }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="currentMsgType === 'like'"
|
||||||
|
class="absolute right-0 top-[30px] hidden group-hover:block text-[#000000] text-[12px] bg-white rounded-lg text-center px-2 py-2 w-20 mt-2 shadow-lg z-10"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in likeParamsTypeList"
|
||||||
|
:style="{
|
||||||
|
color: item.value === likeParamsType ? '#3a75f6' : '',
|
||||||
|
}"
|
||||||
|
:key="index"
|
||||||
|
class="hover:bg-gray-100 py-2 cursor-pointer rounded-lg"
|
||||||
|
@click="(event) => handleSelect(item, 'like')"
|
||||||
|
>
|
||||||
|
{{ item.label }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="p-2 overflow-y-auto" :style="{ height: `calc(100% - 48px)` }">
|
||||||
|
<OfficialMsg v-if="currentMsgType === 'official'" />
|
||||||
|
<ReplyMsg v-if="currentMsgType === 'reply'" :data-list="allList.reply" />
|
||||||
|
<LikeMsg v-if="currentMsgType === 'like'" :data-list="allList.like" />
|
||||||
|
<AttentionMsg
|
||||||
|
v-if="currentMsgType === 'attention'"
|
||||||
|
:data-list="allList.attention"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
.msg-item {
|
||||||
|
@apply h-14 flex items-center mb-2 py-3 pl-6 rounded-lg cursor-pointer;
|
||||||
|
@apply hover:bg-[#f5f6fa];
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
@apply rounded-lg overflow-hidden transition-all duration-300;
|
||||||
|
@apply [box-shadow:0_0_20px_0_rgba(0,0,0,0.05)];
|
||||||
|
@apply hover:bg-[#f5f6fa] hover:[box-shadow:0_0_20px_0_rgba(0,0,0,0.1)];
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,37 +1,462 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Heart } from '@vicons/ionicons5'
|
import { Heart, PersonAddOutline } from '@vicons/ionicons5'
|
||||||
import {
|
|
||||||
Download,
|
|
||||||
Play,
|
|
||||||
} from 'lucide-vue-next'
|
|
||||||
|
|
||||||
|
import {
|
||||||
|
CircleUser,
|
||||||
|
Download,
|
||||||
|
EllipsisVertical, Play,
|
||||||
|
SquarePlus
|
||||||
|
} from 'lucide-vue-next'
|
||||||
|
import { NConfigProvider, NMessageProvider } from 'naive-ui'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
|
const message = useMessage()
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
// 用于版本tabs当前选中的选项卡
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: 'default',
|
layout: 'default',
|
||||||
})
|
})
|
||||||
// const route = useRoute<'publishDetails-id'>()
|
// const userStore = useUserStore()
|
||||||
|
const route = useRoute()
|
||||||
|
const { id } = route.params as { id: string }
|
||||||
|
const activeTab = ref(null)
|
||||||
|
const commentHeight = ref(800)
|
||||||
|
const detailsInfo = ref({})
|
||||||
|
const currentUserInfo = ref<any>({})
|
||||||
|
|
||||||
// const id = route.params.id
|
// 先获取用户信息 再获取版本信息
|
||||||
|
const versionByWorkInfo = ref([])
|
||||||
|
async function getInfo() {
|
||||||
|
try {
|
||||||
|
const res = await request.get(`/model/selectModelById?id=${id}`)
|
||||||
|
if (res.code === 200) {
|
||||||
|
detailsInfo.value = res.data
|
||||||
|
// detailsInfo.value.styleList =JSON.parse(res.data.styleList)
|
||||||
|
// // 1翻译
|
||||||
|
try {
|
||||||
|
const res1 = await request.get(`/ModelVersion/finbyid?id=${res.data.id}`)// 获取版本
|
||||||
|
if (res1.code === 200 && res1.data.length > 0) {
|
||||||
|
versionByWorkInfo.value = res1.data
|
||||||
|
versionByWorkInfo.value.forEach((item) => {
|
||||||
|
item.sampleImagePaths = item.sampleImagePaths.split(',')
|
||||||
|
})
|
||||||
|
nextTick(() => {
|
||||||
|
activeTab.value = versionByWorkInfo.value[0].id
|
||||||
|
})
|
||||||
|
// const commentRes = await request.get(`/WorkFlowComment/comment/${res.data.id}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// // 获取当前作品的用户信息
|
||||||
|
try {
|
||||||
|
const res = await request.get(`/system/user/selectUserById?id=${detailsInfo.value.userId}`)
|
||||||
|
if (res.code === 200) {
|
||||||
|
currentUserInfo.value = res.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
getAttention()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getInfo()
|
||||||
|
|
||||||
|
// 获取用户点赞/粉丝/关注数量
|
||||||
|
interface SelectUserInfo {
|
||||||
|
attention: number
|
||||||
|
bean: number
|
||||||
|
imageLikeNum:number
|
||||||
|
modelDownLoadNum:number
|
||||||
|
modelLikeNum:number
|
||||||
|
modelRunNum:number
|
||||||
|
}
|
||||||
|
const selectUserInfo = ref<SelectUserInfo>({
|
||||||
|
attention: 0,
|
||||||
|
bean: 0,
|
||||||
|
imageLikeNum:0,
|
||||||
|
modelDownLoadNum:0,
|
||||||
|
modelLikeNum:0,
|
||||||
|
modelRunNum:0,
|
||||||
|
})
|
||||||
|
|
||||||
|
// 获取点赞粉丝等的数量
|
||||||
|
async function getAttention() {
|
||||||
|
try {
|
||||||
|
const res = await request.get(`/attention/selectUserInfo?userId=${detailsInfo.value.userId}`)
|
||||||
|
if (res.code === 200) {
|
||||||
|
selectUserInfo.value = res.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 举报/编辑/删除
|
||||||
|
const isDelete = ref(false)
|
||||||
|
async function handleSelect(event: Event, type: string) {
|
||||||
|
event.stopPropagation() // 阻止事件冒泡
|
||||||
|
if (type === 'report') {
|
||||||
|
await request.get(`/WorkFlow/report?id=${id}`) // 举报
|
||||||
|
}
|
||||||
|
else if (type === 'edit') {
|
||||||
|
router.push({
|
||||||
|
path: `/publish-model`,
|
||||||
|
query: {
|
||||||
|
type: 'edit',
|
||||||
|
id: detailsInfo.value.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
isDelete.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
async function onDelete() {
|
||||||
|
try {
|
||||||
|
const res = await request.get(`/model/delete?id=${detailsInfo.value.id}`)
|
||||||
|
if (res.code === 200) {
|
||||||
|
message.success('删除成功')
|
||||||
|
isDelete.value = false
|
||||||
|
router.push('/personal-center')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关注用户/取消关注
|
||||||
|
async function onChangeAttention() {
|
||||||
|
try {
|
||||||
|
const res = await request.get(`/attention/addAttention?userId=${detailsInfo.value.userId}`)
|
||||||
|
if (res.code === 200) {
|
||||||
|
if (res.data) {
|
||||||
|
detailsInfo.value.isAttention = 1
|
||||||
|
message.success('关注成功')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
detailsInfo.value.isAttention = 0
|
||||||
|
message.success('取消关注成功')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点赞
|
||||||
|
async function onLike() {
|
||||||
|
try {
|
||||||
|
const res = await request.get(`/ModelComment/modelLike?modelId=${detailsInfo.value.id}`)
|
||||||
|
if (res.code === 200) {
|
||||||
|
detailsInfo.value.isLike === 0 ? detailsInfo.value.isLike = 1 : detailsInfo.value.isLike = 0
|
||||||
|
if (detailsInfo.value.isLike === 0) {
|
||||||
|
message.success('取消点赞成功')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
message.success('点赞成功')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<div class="w-[1125px]">
|
<div class="w-[1125px] p-4">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="text-[26px] font-bold mr-2">
|
<div class="text-[26px] font-bold mr-4">
|
||||||
名称
|
{{ detailsInfo.modelName }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<n-tooltip trigger="hover">
|
||||||
|
<template #trigger>
|
||||||
|
<div class="flex items-center bg-[#f4f5f9] px-2 rounded-full">
|
||||||
|
<component :is="Play" class="h-[14px] w-[14px] text-black menu-icon m-1" />
|
||||||
|
<span> {{ detailsInfo.reals || 0 }} </span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
在线生成数
|
||||||
|
</n-tooltip>
|
||||||
|
|
||||||
|
<n-tooltip trigger="hover">
|
||||||
|
<template #trigger>
|
||||||
|
<div class="flex items-center bg-[#f4f5f9] px-2 rounded-full mx-4">
|
||||||
|
<component
|
||||||
|
:is="Download"
|
||||||
|
class="h-[14px] w-[14px] text-black menu-icon m-1"
|
||||||
|
/>
|
||||||
|
<span> {{ detailsInfo.numbers || 0 }} </span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
下载数
|
||||||
|
</n-tooltip>
|
||||||
|
|
||||||
|
<n-tooltip trigger="hover">
|
||||||
|
<template #trigger>
|
||||||
|
<div class="flex items-center bg-[#f4f5f9] px-2 rounded-full">
|
||||||
|
<img src="@/assets/img/heart.png" class="w-[14px] h-[14px] mr-1" alt="">
|
||||||
|
<span>{{ detailsInfo.likeNum || 0}} </span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
点赞数
|
||||||
|
</n-tooltip>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center mt-3 mb-5">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in detailsInfo.styleList"
|
||||||
|
:key="index"
|
||||||
|
class="text-[12px] bg-[#ebf2fe] p-1 px-2 text-[#557abf] mr-4 rounded-md"
|
||||||
|
>
|
||||||
|
{{ item }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex w-full gap-1">
|
||||||
|
<div class="w-2/3">
|
||||||
|
<div class="w-full">
|
||||||
|
<n-tabs v-model:value="activeTab" type="line" animated>
|
||||||
|
<n-tab-pane
|
||||||
|
v-for="(item, index) in versionByWorkInfo"
|
||||||
|
:key="index"
|
||||||
|
:name="item.id"
|
||||||
|
:tab="item.versionName"
|
||||||
|
>
|
||||||
|
<!-- 显示最后一步上传图片的图片 -->
|
||||||
|
<div class="grid grid-cols-2 gap-2.5 box-border">
|
||||||
|
<img
|
||||||
|
v-for="(subItem, subIndex) in item.sampleImagePaths"
|
||||||
|
:key="subIndex"
|
||||||
|
:src="subItem"
|
||||||
|
class="w-full h-[300px]"
|
||||||
|
alt=""
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="detailsInfo.original === 1" class="font-bold text-[20px] my-6">
|
||||||
|
转载自作者: {{ detailsInfo.authorName }}
|
||||||
|
</div>
|
||||||
|
<!-- 富文本中输入的文字 图片 -->
|
||||||
|
<div class="w-full mt-2">
|
||||||
|
<div v-html="item.versionDescription" />
|
||||||
|
</div>
|
||||||
|
</n-tab-pane>
|
||||||
|
</n-tabs>
|
||||||
|
</div>
|
||||||
|
<div class="mt-4">
|
||||||
|
<div style="padding: 20px">
|
||||||
|
<NConfigProvider>
|
||||||
|
<NMessageProvider>
|
||||||
|
<BaseComment v-if="detailsInfo.id" type="model" :height="commentHeight" :details-info="detailsInfo" />
|
||||||
|
</NMessageProvider>
|
||||||
|
</NConfigProvider>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="w-1/3 mt-3">
|
||||||
|
<div
|
||||||
|
class="flex justify-between text-[#a3a1a1] text-[12px] items-center -ml-60"
|
||||||
|
>
|
||||||
|
<div class="flex justify-end">
|
||||||
|
<div v-if="detailsInfo.createTime" class="mr-2">
|
||||||
|
首发时间{{ detailsInfo.createTime }}
|
||||||
|
</div>
|
||||||
|
<div v-if="detailsInfo.updateTime">
|
||||||
|
更新时间:{{ detailsInfo.updateTime }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center relative">
|
||||||
|
<n-icon size="20" :color="detailsInfo.isLike === 0 ? '#ccc' : '#ff0000'" @click="onLike">
|
||||||
|
<Heart class="cursor-pointer" />
|
||||||
|
</n-icon>
|
||||||
|
<div class="group relative">
|
||||||
|
<component
|
||||||
|
:is="EllipsisVertical"
|
||||||
|
class="h-[18px] w-[48px] text-[#557abf] cursor-pointer"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="absolute right-0 top-[10px] hidden group-hover:block text-[#000000] text-[12px] bg-white rounded-lg text-center px-2 py-2 w-20 mt-2 shadow-lg z-10"
|
||||||
|
>
|
||||||
|
<div class="menu-item hover:bg-gray-100 py-2 cursor-pointer rounded-lg" @click="(event) => handleSelect(event, 'report')">
|
||||||
|
举报
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="menu-item hover:bg-gray-100 py-2 cursor-pointer rounded-lg"
|
||||||
|
@click="(event) => handleSelect(event, 'edit')"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</div>
|
||||||
|
<div class="menu-item hover:bg-gray-100 py-2 cursor-pointer rounded-lg" @click="(event) => handleSelect(event, 'delete')">
|
||||||
|
删除
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="flex items-center mt-10 p-2 bg-[#f3f5f9] w-full rounded-md h-[80px] box-border"
|
||||||
|
>
|
||||||
|
<div class="w-[70px] h-[70px] rounded-full overflow-hidden mr-4">
|
||||||
|
<client-only>
|
||||||
|
<NAvatar
|
||||||
|
class="w-full h-full mr-2 block"
|
||||||
|
round
|
||||||
|
size="small"
|
||||||
|
:src="currentUserInfo.avatar"
|
||||||
|
/>
|
||||||
|
</client-only>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="flex-1">
|
||||||
|
<client-only>
|
||||||
|
<div class="text[20px] font-bold">
|
||||||
|
{{ currentUserInfo.nickName }}
|
||||||
|
</div>
|
||||||
|
</client-only>
|
||||||
|
<!-- 0代表原创 1代表转载 -->
|
||||||
|
<div v-if="detailsInfo.original === 0" class="text-[14px]">
|
||||||
|
原创作者
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1 flex justify-end">
|
||||||
|
<div class="flex items-center font-bold px-1 justify-center w-24 h-10 rounded-full text-[#426af7] border-2 border-[#426af7] border-solid cursor-pointer" @click="onChangeAttention">
|
||||||
|
<n-icon v-if="detailsInfo.isAttention === 0" size="20" class="mr-2">
|
||||||
|
<PersonAddOutline />
|
||||||
|
</n-icon>
|
||||||
|
{{ detailsInfo.isAttention === 1 ? '已关注' : '关注' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center text-[#969798]">
|
||||||
|
<component
|
||||||
|
:is="CircleUser"
|
||||||
|
class="h-[12px] w-[12px] text-black menu-icon m-1 text-[#969798] m-0"
|
||||||
|
/>
|
||||||
|
<span class="mr-2"> {{ selectUserInfo.bean }} </span>
|
||||||
|
<!-- <component
|
||||||
|
:is="HardDriveUpload"
|
||||||
|
class="h-[12px] w-[14px] text-black menu-icon m-1 text-[#969798]"
|
||||||
|
/>
|
||||||
|
<span class="mr-2"> 2 </span> -->
|
||||||
|
<component
|
||||||
|
:is="Play"
|
||||||
|
class="h-[12px] w-[12px] text-black menu-icon m-1 text-[#969798]"
|
||||||
|
/>
|
||||||
|
<span class="mr-2"> {{ selectUserInfo.modelRunNum }} </span>
|
||||||
|
<component
|
||||||
|
:is="Download"
|
||||||
|
class="h-[12px] w-[12px] text-black menu-icon m-1 text-[#969798]"
|
||||||
|
/>
|
||||||
|
<span class="mr-2"> {{ selectUserInfo.modelDownloadNum }} </span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 不支持的bg #b1b2b2 -->
|
||||||
|
<div
|
||||||
|
class="flex items-center justify-center mt-4 w-full text-white bg-[#3c7af6] w-full rounded-md h-[50px] cursor-pointer"
|
||||||
|
>
|
||||||
|
<component
|
||||||
|
:is="Play"
|
||||||
|
class="h-[20px] w-[20px] text-white menu-icon m-1 text-[#969798]"
|
||||||
|
/>
|
||||||
|
<span class="mr-1"> 立即生图</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-y-2">
|
||||||
|
<div class="flex flex-1 items-center justify-center bg-[#eceef4] h-[50px] mt-4 mr-1 rounded-md">
|
||||||
|
<component
|
||||||
|
:is="SquarePlus"
|
||||||
|
class="h-[20px] w-[20px] text-black menu-icon m-1 text-[#969798]"
|
||||||
|
/>
|
||||||
|
<span class="mr-1">加入模型课</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="flex flex-1 items-center bg-gradient-to-r from-[#ffe9c8] to-[#ffd264] justify-center ml-1 mt-4 text-black bg-[#eceef4] w-full rounded-md h-[50px] cursor-pointer"
|
||||||
|
>
|
||||||
|
<component
|
||||||
|
:is="Download"
|
||||||
|
class="h-[20px] w-[20px] text-black menu-icon m-1 text-[#969798]"
|
||||||
|
/>
|
||||||
|
<span class="mr-1"> 下载 (122.22MB) </span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- <div style="background: linear-gradient(135deg,#3cc9ff, #8fa6ff, 41%, #d8b4ff 74%,#326bff)" class="flex items-center justify-center mt-4 w-full h-14 text-black bg-[#fff] w-full rounded-md h-[80px] cursor-pointer hover:bg-[#f1f2f7]">
|
||||||
|
<component :is="Download" class="h-[20px] w-[20px] text-black menu-icon m-1 text-[#969798]" />
|
||||||
|
<span class="mr-1">
|
||||||
|
下载客户端
|
||||||
|
</span>
|
||||||
|
</div> -->
|
||||||
|
<n-modal
|
||||||
|
v-model:show="isDelete"
|
||||||
|
:mask-closable="false"
|
||||||
|
preset="dialog"
|
||||||
|
title="提示!"
|
||||||
|
content="确定要将模型删除? 模型删除后无法找回"
|
||||||
|
negative-text="取消"
|
||||||
|
positive-text="确认"
|
||||||
|
@negative-click="onDelete"
|
||||||
|
@positive-click="onDelete"
|
||||||
|
/>
|
||||||
|
<div />
|
||||||
</div>
|
</div>
|
||||||
<component :is="Play" class="h-[14px] w-[14px] text-white menu-icon m-1" />
|
|
||||||
<span>
|
|
||||||
<!-- {{ item.reals }} -->0
|
|
||||||
</span>
|
|
||||||
<component
|
|
||||||
:is="Download"
|
|
||||||
class="h-[14px] w-[14px] text-white menu-icon m-1"
|
|
||||||
/>
|
|
||||||
<span>
|
|
||||||
<!-- {{ item.numbers }} -->0
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.header-num {
|
||||||
|
@apply flex items-center bg-[#f4f5f9] px-2 rounded-full;
|
||||||
|
}
|
||||||
|
.header-tag {
|
||||||
|
@apply flex items-center bg-[#f4f5f9] px-2 rounded-full;
|
||||||
|
display: flex;
|
||||||
|
align-self: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.n-tabs.n-tabs--line-type .n-tabs-tab.n-tabs-tab,
|
||||||
|
.n-tabs.n-tabs--bar-type .n-tabs-tab.n-tabs-tab {
|
||||||
|
color: #949494;
|
||||||
|
}
|
||||||
|
.n-tabs.n-tabs--line-type .n-tabs-tab.n-tabs-tab--active,
|
||||||
|
.n-tabs.n-tabs--bar-type .n-tabs-tab.n-tabs-tab--active {
|
||||||
|
color: #000;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.n-tabs .n-tabs-bar {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
height: 4px;
|
||||||
|
background: linear-gradient(90deg, #173eff 0%, #1b7dff 100%);
|
||||||
|
border-radius: 2px;
|
||||||
|
transition:
|
||||||
|
left 0.2s var(--n-bezier),
|
||||||
|
max-width 0.2s var(--n-bezier),
|
||||||
|
opacity 0.3s var(--n-bezier),
|
||||||
|
background-color 0.3s var(--n-bezier);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -1,47 +1,246 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { commonApi } from "@/api/common";
|
||||||
|
import { ArrowBack, ArrowForward } from '@vicons/ionicons5';
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: 'default',
|
layout: "default",
|
||||||
|
});
|
||||||
|
const params = ref({
|
||||||
|
name: null,
|
||||||
|
order: null,
|
||||||
|
type: null
|
||||||
})
|
})
|
||||||
|
// const hotNameList = ref([
|
||||||
|
// '哪吒',
|
||||||
|
// '电商',
|
||||||
|
// '产品',
|
||||||
|
// '海报',
|
||||||
|
// '国潮',
|
||||||
|
// '写真',
|
||||||
|
// '推文',
|
||||||
|
// 'XL',
|
||||||
|
// ])
|
||||||
|
const listRef = ref(null);
|
||||||
|
// function handleSearch() {
|
||||||
|
// onSearch()
|
||||||
|
// }
|
||||||
|
const modelCategoryList = ref([]);
|
||||||
|
async function getDictType() {
|
||||||
|
try {
|
||||||
|
const res = await commonApi.dictType({ type: "model_child_category" });
|
||||||
|
if (res.code === 200) {
|
||||||
|
modelCategoryList.value = res.data;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getDictType();
|
||||||
|
|
||||||
const myItems = [1, 2, 3, 4, 5, 6, 7]
|
//执行搜索的时候
|
||||||
const scrollListRef = ref(null)
|
function onSearch() {
|
||||||
|
if (listRef.value) {
|
||||||
// 处理选中项改变
|
listRef.value?.initPageNUm();
|
||||||
function handleChange(index) {
|
}
|
||||||
console.log('Current index:', index)
|
}
|
||||||
|
// 切换类型
|
||||||
|
function changeCategory(item:any){
|
||||||
|
params.value.type = item.dictValue
|
||||||
|
onSearch()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理项目点击
|
// function onHot(name:string){
|
||||||
function handleItemClick({ index, item }) {
|
// params.value.name = name
|
||||||
console.log('Clicked item:', item, 'at index:', index)
|
// onSearch()
|
||||||
}
|
// }
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="p-6">
|
<div class="p-6">
|
||||||
<h1 class="text-2xl font-bold">
|
<div class="header flex h-[150px] border-b border-b-[#ebebeb]">
|
||||||
模型广场
|
<div class="flex-1 py-2">
|
||||||
</h1>
|
<div class="max-w-4xl mx-auto">
|
||||||
<!-- <ScrollListView
|
<div class="relative flex items-center w-full">
|
||||||
ref="scrollListRef"
|
<!-- 搜索输入框 -->
|
||||||
title="My Scroll List"
|
<input
|
||||||
:items="myItems"
|
type="text"
|
||||||
:initial-index="2"
|
class="w-full py-3 px-6 text-lg text-gray-600 placeholder-gray-400 bg-white border-2 border-blue-500 rounded-full focus:outline-none focus:border-blue-600"
|
||||||
@change="handleChange"
|
placeholder="搜索模型/图片/创作者寻找灵感"
|
||||||
@item-click="handleItemClick"
|
@keyup.enter="onSearch"
|
||||||
>
|
v-model="params.name"
|
||||||
<template #item="{ item }">
|
/>
|
||||||
<div class="custom-item">
|
|
||||||
<span>{{ item }}</span>
|
<!-- 相机图标 -->
|
||||||
|
<button class="absolute right-24 p-2 mr-2 text-gray-400 hover:text-gray-600">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-6 w-6"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M15 13a3 3 0 11-6 0 3 3 0 016 0z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- 搜索按钮 -->
|
||||||
|
<button
|
||||||
|
class="absolute right-0 px-8 py-3 mr-2 text-white bg-blue-500 rounded-full hover:bg-blue-600 focus:outline-none"
|
||||||
|
@click="onSearch"
|
||||||
|
>
|
||||||
|
搜索
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
<!-- <div class="flex p-3">
|
||||||
</ScrollListView> -->
|
<div v-for="(item, index) in hotNameList" @click="onHot(item)" :key="index" class="mr-3 mb-2 cursor-pointer">
|
||||||
<!-- <button @click="handleTest">测试</button>
|
{{ item }}
|
||||||
<client-only>
|
</div>
|
||||||
{{ useUser.brief }}
|
</div> -->
|
||||||
</client-only> -->
|
</div>
|
||||||
<!-- 这里添加模型广场的具体内容 -->
|
<div class="w-[500px] ml-4">
|
||||||
|
<!-- <img
|
||||||
|
class="h-[130px] w-full rounded-lg"
|
||||||
|
src="https://img.zcool.cn/tubelocation/e41767ac6706cd0109100099e04b.jpg?imageMogr2/format/webp"
|
||||||
|
alt=""
|
||||||
|
/> -->
|
||||||
|
<n-carousel show-arrow autoplay>
|
||||||
|
<img
|
||||||
|
class="carousel-img"
|
||||||
|
src="https://naive-ui.oss-cn-beijing.aliyuncs.com/carousel-img/carousel1.jpeg"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
class="carousel-img"
|
||||||
|
src="https://naive-ui.oss-cn-beijing.aliyuncs.com/carousel-img/carousel2.jpeg"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
class="carousel-img"
|
||||||
|
src="https://naive-ui.oss-cn-beijing.aliyuncs.com/carousel-img/carousel3.jpeg"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
class="carousel-img"
|
||||||
|
src="https://naive-ui.oss-cn-beijing.aliyuncs.com/carousel-img/carousel4.jpeg"
|
||||||
|
/>
|
||||||
|
<template #arrow="{ prev, next }">
|
||||||
|
<div class="custom-arrow">
|
||||||
|
<button type="button" class="custom-arrow--left" @click="prev">
|
||||||
|
<n-icon><ArrowBack /></n-icon>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="custom-arrow--right" @click="next">
|
||||||
|
<n-icon><ArrowForward /></n-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #dots="{ total, currentIndex, to }">
|
||||||
|
<ul class="custom-dots">
|
||||||
|
<li
|
||||||
|
v-for="index of total"
|
||||||
|
:key="index"
|
||||||
|
:class="{ ['is-active']: currentIndex === index - 1 }"
|
||||||
|
@click="to(index - 1)"
|
||||||
|
/>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
</n-carousel>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-wrap">
|
||||||
|
<div class="flex flex-wrap">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in modelCategoryList"
|
||||||
|
:key="index"
|
||||||
|
:style="{color: item.dictValue === params.type ? '#000' : '#6f6f6f'}"
|
||||||
|
@click="changeCategory(item)"
|
||||||
|
class="pt-4 mr-4 cursor-pointer"
|
||||||
|
>
|
||||||
|
{{ item.dictLabel }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-wrap">
|
||||||
|
<ModelList ref="listRef" :params="params"></ModelList>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.grid > div {
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
.grid > div:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
.carousel-img {
|
||||||
|
width: 100%;
|
||||||
|
height: 120px;
|
||||||
|
border-radius: 6px;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
.custom-arrow {
|
||||||
|
display: flex;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 25px;
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-arrow button {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
margin-right: 12px;
|
||||||
|
color: #fff;
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
border-width: 0;
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-arrow button:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-arrow button:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
transform-origin: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-dots {
|
||||||
|
display: flex;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 20px;
|
||||||
|
left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-dots li {
|
||||||
|
display: inline-block;
|
||||||
|
width: 12px;
|
||||||
|
height: 4px;
|
||||||
|
margin: 0 3px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: rgba(255, 255, 255, 0.4);
|
||||||
|
transition:
|
||||||
|
width 0.3s,
|
||||||
|
background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-dots li.is-active {
|
||||||
|
width: 40px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { commonApi } from '@/api/common'
|
||||||
import EditUserInfo from '@/components/EditUserInfo.vue'
|
import EditUserInfo from '@/components/EditUserInfo.vue'
|
||||||
import { useUserStore } from '@/stores/user'
|
import { useUserStore } from '@/stores/user'
|
||||||
import { formatDate } from '@/utils/index.ts'
|
import { formatDate } from '@/utils/index.ts'
|
||||||
|
import { NConfigProvider, NMessageProvider } from 'naive-ui'
|
||||||
import { nextTick, onMounted, onUnmounted, ref } from 'vue'
|
import { nextTick, onMounted, onUnmounted, ref } from 'vue'
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
@ -341,6 +342,11 @@ const attentionIsVisible = ref<boolean>(false)
|
||||||
function onCloseAttentionModel() {
|
function onCloseAttentionModel() {
|
||||||
attentionIsVisible.value = false
|
attentionIsVisible.value = false
|
||||||
}
|
}
|
||||||
|
function onClearDate(){
|
||||||
|
publishParams.value.startTime = '',
|
||||||
|
publishParams.value.endTime = '',
|
||||||
|
initPageNUm()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -492,6 +498,7 @@ function onCloseAttentionModel() {
|
||||||
range-separator="至"
|
range-separator="至"
|
||||||
start-placeholder="开始日期"
|
start-placeholder="开始日期"
|
||||||
end-placeholder="结束日期"
|
end-placeholder="结束日期"
|
||||||
|
:on-clear="onClearDate"
|
||||||
clearable
|
clearable
|
||||||
@update:value="changeDate"
|
@update:value="changeDate"
|
||||||
/>
|
/>
|
||||||
|
@ -511,10 +518,13 @@ function onCloseAttentionModel() {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<NConfigProvider>
|
||||||
|
<NMessageProvider>
|
||||||
|
<Authentication ref="authenticationRef" />
|
||||||
|
<EditUserInfo ref="editUserInfoRef" />
|
||||||
|
</NMessageProvider>
|
||||||
|
</NConfigProvider>
|
||||||
<!-- Dialog Components -->
|
<!-- Dialog Components -->
|
||||||
<Authentication ref="authenticationRef" />
|
|
||||||
<EditUserInfo ref="editUserInfoRef" />
|
|
||||||
|
|
||||||
<div class="login-content my-4 grid grid-cols-4 gap-4 px-5">
|
<div class="login-content my-4 grid grid-cols-4 gap-4 px-5">
|
||||||
<PersonalCenterCard
|
<PersonalCenterCard
|
||||||
|
|
|
@ -0,0 +1,189 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { commonApi } from "@/api/common";
|
||||||
|
import { ArrowBack, ArrowForward } from "@vicons/ionicons5";
|
||||||
|
import { NConfigProvider, NMessageProvider } from "naive-ui";
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
layout: "default",
|
||||||
|
});
|
||||||
|
const params = ref({
|
||||||
|
name: null,
|
||||||
|
order: null,
|
||||||
|
type: null,
|
||||||
|
});
|
||||||
|
const listRef = ref(null);
|
||||||
|
// function handleSearch() {
|
||||||
|
// onSearch()
|
||||||
|
// }
|
||||||
|
|
||||||
|
const modelCategoryList = ref([]);
|
||||||
|
async function getDictType() {
|
||||||
|
try {
|
||||||
|
const res = await commonApi.dictType({ type: "image_label" });
|
||||||
|
if (res.code === 200) {
|
||||||
|
modelCategoryList.value = res.data;
|
||||||
|
modelCategoryList.value.unshift({
|
||||||
|
dictValue: null,
|
||||||
|
dictLabel: "全部",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getDictType();
|
||||||
|
|
||||||
|
//执行搜索的时候
|
||||||
|
function onSearch() {
|
||||||
|
if (listRef.value) {
|
||||||
|
listRef.value?.initPageNUm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 切换类型
|
||||||
|
function changeCategory(item: any) {
|
||||||
|
params.value.type = item.dictValue;
|
||||||
|
onSearch();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="p-6">
|
||||||
|
<div class="w-full">
|
||||||
|
<n-carousel show-arrow autoplay>
|
||||||
|
<img
|
||||||
|
class="carousel-img"
|
||||||
|
src="https://naive-ui.oss-cn-beijing.aliyuncs.com/carousel-img/carousel1.jpeg"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
class="carousel-img"
|
||||||
|
src="https://naive-ui.oss-cn-beijing.aliyuncs.com/carousel-img/carousel2.jpeg"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
class="carousel-img"
|
||||||
|
src="https://naive-ui.oss-cn-beijing.aliyuncs.com/carousel-img/carousel3.jpeg"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
class="carousel-img"
|
||||||
|
src="https://naive-ui.oss-cn-beijing.aliyuncs.com/carousel-img/carousel4.jpeg"
|
||||||
|
/>
|
||||||
|
<template #arrow="{ prev, next }">
|
||||||
|
<div class="custom-arrow">
|
||||||
|
<button type="button" class="custom-arrow--left" @click="prev">
|
||||||
|
<n-icon><ArrowBack /></n-icon>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="custom-arrow--right" @click="next">
|
||||||
|
<n-icon><ArrowForward /></n-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #dots="{ total, currentIndex, to }">
|
||||||
|
<ul class="custom-dots">
|
||||||
|
<li
|
||||||
|
v-for="index of total"
|
||||||
|
:key="index"
|
||||||
|
:class="{ ['is-active']: currentIndex === index - 1 }"
|
||||||
|
@click="to(index - 1)"
|
||||||
|
/>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
</n-carousel>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center mt-4">
|
||||||
|
<div class="text-[20px] mr-4">作品灵感</div>
|
||||||
|
<HeaderSearchInput v-model="params.name" @search="onSearch" />
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-wrap mt-2">
|
||||||
|
<div class="flex flex-wrap">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in modelCategoryList"
|
||||||
|
:key="index"
|
||||||
|
:style="{
|
||||||
|
color: item.dictValue === params.type ? '#fff' : '#000',
|
||||||
|
background: item.dictValue === params.type ? '#000' : '#fff',
|
||||||
|
}"
|
||||||
|
@click="changeCategory(item)"
|
||||||
|
class="px-2 py-1 rounded-full mr-4 cursor-pointer"
|
||||||
|
>
|
||||||
|
{{ item.dictLabel }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-wrap">
|
||||||
|
<NConfigProvider>
|
||||||
|
<NMessageProvider>
|
||||||
|
<PictureList ref="listRef" :params="params" />
|
||||||
|
</NMessageProvider>
|
||||||
|
</NConfigProvider>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.grid > div {
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
.grid > div:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
.carousel-img {
|
||||||
|
width: 100%;
|
||||||
|
height: 180px;
|
||||||
|
border-radius: 10px;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-arrow {
|
||||||
|
display: flex;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 25px;
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-arrow button {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
margin-right: 12px;
|
||||||
|
color: #fff;
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
border-width: 0;
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-arrow button:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-arrow button:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
transform-origin: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-dots {
|
||||||
|
display: flex;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 20px;
|
||||||
|
left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-dots li {
|
||||||
|
display: inline-block;
|
||||||
|
width: 12px;
|
||||||
|
height: 4px;
|
||||||
|
margin: 0 3px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: rgba(255, 255, 255, 0.4);
|
||||||
|
transition: width 0.3s, background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-dots li.is-active {
|
||||||
|
width: 40px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,215 +1,166 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { FormInst, FormItemRule } from 'naive-ui'
|
import CreateModels from "@/components/publishModel/CreateModels.vue";
|
||||||
import CreateModels from '@/components/publishModel/CreateModels.vue'
|
import EditVersion from "@/components/publishModel/EditVersion.vue";
|
||||||
import EditVersion from '@/components/publishModel/EditVersion.vue'
|
import UploadImg from "@/components/publishModel/UploadImg.vue";
|
||||||
|
import { NConfigProvider, NMessageProvider } from "naive-ui";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
|
||||||
const step2Ref = ref(null)
|
const route = useRoute();
|
||||||
const currentStep = ref(2)
|
const { type, id } = route.query;
|
||||||
|
const step2Ref = ref(null);
|
||||||
|
const currentStep = ref(1);
|
||||||
const formData = ref({
|
const formData = ref({
|
||||||
modelProduct: {
|
modelProduct: {},
|
||||||
modelName: '模型名称',
|
modelVersionList: [],
|
||||||
modelType: null, // ,,模型类型
|
});
|
||||||
category: null, // 垂类分类
|
|
||||||
functions: null, // '模型功能',
|
|
||||||
tags: null, // 标签(最多三个,切割)string
|
|
||||||
activityId: null, // 参与活动string
|
|
||||||
|
|
||||||
isOriginal: 1,
|
async function initFormData() {
|
||||||
originalAuthorName: '',
|
if (type === "add") {
|
||||||
},
|
formData.value = {
|
||||||
|
|
||||||
modelVersionList: [
|
|
||||||
{
|
|
||||||
versionName: '', // 版本名称
|
|
||||||
modelId: null, // 基础模型
|
|
||||||
versionDescription: '"<p>这是一个描述</p><p><img src=\"https://img1.baidu.com/it/u=3001150338,397170470&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1422\" /></p><p>这是两张图片之间的一些文字说明</p><p><img src=\"https://img12.iqilu.com/10339/clue/202405/29/68ec17f5-9621-461f-ad22-a6820a3f9cf5.jpg\" /></p>"', // 版本描述
|
|
||||||
filePath: 'https://ybl2112.oss-cn-beijing.aliyuncs.com/2025/JANUARY/2/19/4/877e449c-3c0d-4630-a304-91ec110499f2.png', // 文件路径
|
|
||||||
fileName: '这是一个文件名文件名。。。。这是一个文件名文件这是一个文件名文件这是一个文件名文件这是一个文件名文件', // 文档里没有 ,表里没有
|
|
||||||
sampleImagePaths: 'https://ybl2112.oss-cn-beijing.aliyuncs.com/2025/JANUARY/2/19/4/877e449c-3c0d-4630-a304-91ec110499f2.png,https://ybl2112.oss-cn-beijing.aliyuncs.com/2025/JANUARY/2/19/4/877e449c-3c0d-4630-a304-91ec110499f2.png,https://ybl2112.oss-cn-beijing.aliyuncs.com/2025/JANUARY/2/19/4/877e449c-3c0d-4630-a304-91ec110499f2.png', // 第三部的图片路径最多20张,切割
|
|
||||||
triggerWords: '触发词', // 触发词
|
|
||||||
isPublic: 1, // 权限是否公开权限 1公开 2自见
|
|
||||||
isFree: 0, // 0免费
|
|
||||||
allowFusion: 1, // 待确定
|
|
||||||
isOnlineUse: 0, // 是否允许在线使用
|
|
||||||
allowDownloadImage: 1, // 允许下载生图
|
|
||||||
allowUsage: 1, // 是否允许使用
|
|
||||||
allowSoftwareUse: 1, // 允许在软件旗下使用
|
|
||||||
allowCommercialUse: 1, // 是否允许商用
|
|
||||||
// 允许模型转售或者融合手出售字段没找到?
|
|
||||||
isExclusiveModel: 0, // 是否为独家模型这个字段
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
async function addWorkflow() {
|
|
||||||
try {
|
|
||||||
const params = {
|
|
||||||
modelProduct: {
|
modelProduct: {
|
||||||
modelName: '模型名称',
|
modelName: "",
|
||||||
modelTypeId: 1, // ,,模型类型
|
modelType: null, // ,,模型类型
|
||||||
category: '1', // 垂类分类
|
category: null, // 垂类分类
|
||||||
functions: '1', // '模型功能',
|
functions: null, // '模型功能',
|
||||||
tags: '11', // 标签(最多三个,切割)string
|
tags: '', // 标签(最多三个,切割)string
|
||||||
activityId: '1', // 参与活动string
|
tagsList:[],
|
||||||
|
activityId: null, // 参与活动string
|
||||||
isOriginal: 1, // ??? 0原创1非原创
|
isOriginal: 1, // 几代表原创
|
||||||
originalAuthorName: '作者名称',
|
originalAuthorName: "",
|
||||||
},
|
},
|
||||||
|
|
||||||
modelVersionList: [
|
modelVersionList: [
|
||||||
{
|
{
|
||||||
versionName: '1.0', // 版本名称
|
delFlag: "0", // 0代表存在 2代表删除
|
||||||
modelId: 1, // 基础模型
|
versionName: "", // 版本名称
|
||||||
versionDescription: '"<p>这是一个描述</p><p><img src=\"https://img1.baidu.com/it/u=3001150338,397170470&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1422\" /></p><p>这是两张图片之间的一些文字说明</p><p><img src=\"https://img12.iqilu.com/10339/clue/202405/29/68ec17f5-9621-461f-ad22-a6820a3f9cf5.jpg\" /></p>"', // 版本描述
|
modelVersionType: null, // 基础模型
|
||||||
filePath: 'https://ybl2112.oss-cn-beijing.aliyuncs.com/2025/JANUARY/2/19/4/877e449c-3c0d-4630-a304-91ec110499f2.png', // 文件路径
|
filePath: "", // 文件路径
|
||||||
fileName: '', // 文档里没有 ,表里没有
|
fileName: "", // 文档里没有 ,表里没有
|
||||||
sampleImagePaths: 'https://ybl2112.oss-cn-beijing.aliyuncs.com/2025/JANUARY/2/19/4/877e449c-3c0d-4630-a304-91ec110499f2.png,https://ybl2112.oss-cn-beijing.aliyuncs.com/2025/JANUARY/2/19/4/877e449c-3c0d-4630-a304-91ec110499f2.png,https://ybl2112.oss-cn-beijing.aliyuncs.com/2025/JANUARY/2/19/4/877e449c-3c0d-4630-a304-91ec110499f2.png', // 第三部的图片路径最多20张,切割
|
versionDescription: "", // 版本描述
|
||||||
triggerWords: '触发词', // 触发词
|
|
||||||
isPublic: 1, // 权限是否公开权限 1公开 2自见
|
sampleImagePaths: [], // 第三部的图片路径最多20张,切割
|
||||||
allowFusion: 1, // 待确定
|
triggerWords: "", // 触发词
|
||||||
isFree: 0, // 0免费
|
isPublic: 1, // 权限是否公开权限 1公开 0自见
|
||||||
allowDownloadImage: 1, // 允许下载生图
|
isFree: 0, // 是否免费
|
||||||
allowUsage: 1, // 是否允许使用
|
|
||||||
allowSoftwareUse: 1, // 允许在软件旗下使用
|
isOnlineUse: 1, // 是否允许在线使用
|
||||||
allowCommercialUse: 1, // 是否允许商用
|
allowDownloadImage: 1, // 允许下载生图
|
||||||
|
allowSoftwareUse: 1, // 允许在软件旗下使用
|
||||||
|
allowFusion: 1, // 允许进行融合
|
||||||
|
allowCommercialUse: 1, // 是否允许商用
|
||||||
// 允许模型转售或者融合手出售字段没找到?
|
// 允许模型转售或者融合手出售字段没找到?
|
||||||
isExclusiveModel: 1, // 是否为独家模型这个字段
|
isExclusiveModel: 0, // 是否为独家模型这个字段
|
||||||
|
hideImageGenInfo:0, //隐藏图片生成信息
|
||||||
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
const res = await request.get(`/model/finbyid?id=${id}`);
|
||||||
|
formData.value = res.data;
|
||||||
|
if(res.data.modelProduct.modelType && res.data.modelProduct.modelType != 0){
|
||||||
|
formData.value.modelProduct.modelType = res.data.modelProduct.modelType.toString();
|
||||||
|
}
|
||||||
|
if(res.data.modelProduct.activityId && res.data.modelProduct.activityId != 0){
|
||||||
|
formData.value.modelProduct.activityId = Number(res.data.modelProduct.activityId);
|
||||||
|
}
|
||||||
|
if(formData.value.modelProduct.tags){
|
||||||
|
formData.value.modelProduct.tagsList = JSON.parse(formData.value.modelProduct.tags);
|
||||||
|
}else{
|
||||||
|
formData.value.modelProduct.tagsList = []
|
||||||
|
}
|
||||||
|
if(formData.value.modelVersionList.length > 0){
|
||||||
|
for(let i = 0; i < formData.value.modelVersionList.length; i ++){
|
||||||
|
formData.value.modelVersionList[i].sampleImagePaths = formData.value.modelVersionList[i].sampleImagePaths.split(',')
|
||||||
|
formData.value.modelVersionList[i].modelVersionType = formData.value.modelVersionList[i].modelVersionType.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if (formData.value.workFlow.type) {
|
||||||
|
// formData.value.workFlow.typeList = formData.value.workFlow.type.split(',')
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// formData.value.workFlow.typeList = []
|
||||||
|
// }
|
||||||
|
// if (formData.value.workFlow.activityParticipation) {
|
||||||
|
// formData.value.workFlow.activityParticipation = Number(formData.value.workFlow.activityParticipation)
|
||||||
|
// }
|
||||||
|
// for (let i = 0; i < formData.value.workFlowVersionList.length; i++) {
|
||||||
|
// if (formData.value.workFlowVersionList[i].imagePaths) {
|
||||||
|
// formData.value.workFlowVersionList[i].imagePaths = formData.value.workFlowVersionList[i].imagePaths.split(',')
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
}
|
}
|
||||||
const res = await request.post('/model/insert', params)
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
console.log(error)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function createModelsNext() {
|
initFormData();
|
||||||
currentStep.value = 2
|
|
||||||
|
const timeLineList = ref([
|
||||||
|
{
|
||||||
|
name: "创建模型",
|
||||||
|
index: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "编辑版本",
|
||||||
|
index: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "上传图片",
|
||||||
|
index: 3,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
function nextStep() {
|
||||||
|
currentStep.value += 1;
|
||||||
|
}
|
||||||
|
function prevStep() {
|
||||||
|
currentStep.value -= 1;
|
||||||
}
|
}
|
||||||
function handleAddVersion() {
|
function handleAddVersion() {
|
||||||
if (step2Ref.value) {
|
if (step2Ref.value) {
|
||||||
step2Ref.value?.addVersion()
|
step2Ref.value?.addVersion();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// const handleValidateButtonClick = (e: MouseEvent) => {
|
|
||||||
// e.preventDefault();
|
|
||||||
// formRef.value?.validate((errors) => {
|
|
||||||
// if (!errors) {
|
|
||||||
// message.success("验证成功");
|
|
||||||
// } else {
|
|
||||||
// console.log(errors);
|
|
||||||
// message.error("验证失败");
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// };
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="mx-auto py-6">
|
<div class="mx-auto py-6">
|
||||||
<div class="container mx-auto w-[700px]">
|
<div class="container mx-auto w-[700px]">
|
||||||
<div class="step-line flex">
|
<div class="flex items-center justify-between mb-4">
|
||||||
<div class="mr-10">
|
<div class="w-[60%]">
|
||||||
1创建模版
|
<TimeLine :time-line-list="timeLineList" :current-step="currentStep" />
|
||||||
</div>
|
</div>
|
||||||
<div class="mr-10">
|
<div
|
||||||
2编辑版本
|
v-if="currentStep === 2"
|
||||||
</div>
|
class="cursor-pointer flex items-center justify-center rounded-full border border-solid border-[#000] px-4 py-1"
|
||||||
<div class="mr-10">
|
@click="handleAddVersion"
|
||||||
3上传图片
|
>
|
||||||
</div>
|
<CirclePlus class="mr-2" />
|
||||||
<div v-if="currentStep === 2" @click="handleAddVersion">
|
|
||||||
添加版本
|
添加版本
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-container">
|
<div class="form-container">
|
||||||
<div v-if="currentStep === 1" class="first-step">
|
<NConfigProvider>
|
||||||
<CreateModels v-model="formData" @create-models-next="createModelsNext" />
|
<NMessageProvider>
|
||||||
</div>
|
<div v-if="currentStep === 1" class="first-step">
|
||||||
|
<CreateModels v-model="formData" @create-models-next="nextStep" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-if="currentStep === 2" class="second-step">
|
<div v-if="currentStep === 2" class="second-step">
|
||||||
<EditVersion ref="step2Ref" v-model="formData" />
|
<EditVersion
|
||||||
</div>
|
ref="step2Ref"
|
||||||
|
v-model="formData"
|
||||||
<div v-if="currentStep === 3" class="third-step">
|
@prev-step="prevStep"
|
||||||
3
|
@next-step="nextStep"
|
||||||
</div>
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="currentStep === 3" class="third-step">
|
||||||
|
<UploadImg v-model="formData" @pre-step="prevStep" />
|
||||||
|
</div>
|
||||||
|
</NMessageProvider>
|
||||||
|
</NConfigProvider>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="flex items-center justify-center mt-4">
|
|
||||||
<div>
|
|
||||||
<div class="flex justify-center items-center mt-5 text-white w-30 h-10 rounded-lg bg-[#3162ff] cursor-pointer" @click="nextStep">
|
|
||||||
下一步
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
</div>
|
||||||
<!-- <n-form
|
|
||||||
ref="formRef"
|
|
||||||
:model="formData"
|
|
||||||
:rules="rules"
|
|
||||||
label-placement="left"
|
|
||||||
label-width="auto"
|
|
||||||
require-mark-placement="right-hanging"
|
|
||||||
size="large"
|
|
||||||
:style="{
|
|
||||||
maxWidth: '640px',
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<n-form-item label="模型名称" path="inputValue">
|
|
||||||
<n-input v-model:value="formData.inputValue" placeholder="Input" />
|
|
||||||
</n-form-item>
|
|
||||||
<n-form-item label="模型类型" path="inputValue">
|
|
||||||
<n-select v-model:value="formData.value" :options="cuileioptions" />
|
|
||||||
</n-form-item>
|
|
||||||
<n-form-item label="Checkbox Group" path="checkboxGroupValue">
|
|
||||||
<n-checkbox-group v-model:value="formData.checkboxGroupValue">
|
|
||||||
<n-space>
|
|
||||||
<n-checkbox value="Option 1">
|
|
||||||
Option 1
|
|
||||||
</n-checkbox>
|
|
||||||
<n-checkbox value="Option 2">
|
|
||||||
Option 2
|
|
||||||
</n-checkbox>
|
|
||||||
<n-checkbox value="Option 3">
|
|
||||||
Option 3
|
|
||||||
</n-checkbox>
|
|
||||||
</n-space>
|
|
||||||
</n-checkbox-group>
|
|
||||||
</n-form-item>
|
|
||||||
<n-form-item label="Radio Group" path="radioGroupValue">
|
|
||||||
<n-radio-group v-model:value="formData.radioGroupValue" name="radiogroup1">
|
|
||||||
<n-space>
|
|
||||||
<n-radio value="Radio 1">
|
|
||||||
Radio 1
|
|
||||||
</n-radio>
|
|
||||||
<n-radio value="Radio 2">
|
|
||||||
Radio 2
|
|
||||||
</n-radio>
|
|
||||||
<n-radio value="Radio 3">
|
|
||||||
Radio 3
|
|
||||||
</n-radio>
|
|
||||||
</n-space>
|
|
||||||
</n-radio-group>
|
|
||||||
</n-form-item>
|
|
||||||
<n-form-item label="Radio Button Group" path="radioGroupValue">
|
|
||||||
<n-radio-group v-model:value="formData.radioGroupValue" name="radiogroup2">
|
|
||||||
<n-radio-button value="Radio 1">
|
|
||||||
Radio 1
|
|
||||||
</n-radio-button>
|
|
||||||
<n-radio-button value="Radio 2">
|
|
||||||
Radio 2
|
|
||||||
</n-radio-button>
|
|
||||||
<n-radio-button value="Radio 3">
|
|
||||||
Radio 3
|
|
||||||
</n-radio-button>
|
|
||||||
</n-radio-group>
|
|
||||||
</n-form-item>
|
|
||||||
<div style="display: flex; justify-content: flex-end">
|
|
||||||
<n-button round type="primary" @click="addWorkflow">
|
|
||||||
验证
|
|
||||||
</n-button>
|
|
||||||
</div>
|
|
||||||
</n-form> -->
|
|
||||||
</div>
|
</div>
|
||||||
<!-- <pre
|
|
||||||
>{{ JSON.stringify(model, null, 2) }}
|
|
||||||
</pre> -->
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,101 +1,95 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import EditVersion from '@/components/publishWorkFlow/EditVersion.vue'
|
import EditVersion from "@/components/publishWorkFlow/EditVersion.vue";
|
||||||
import EditWorkFlow from '@/components/publishWorkFlow/EditWorkFlow.vue'
|
import EditWorkFlow from "@/components/publishWorkFlow/EditWorkFlow.vue";
|
||||||
import UploadImg from '@/components/publishWorkFlow/UploadImg.vue'
|
import UploadImg from "@/components/publishWorkFlow/UploadImg.vue";
|
||||||
import { CirclePlus } from 'lucide-vue-next'
|
import { CirclePlus } from "lucide-vue-next";
|
||||||
import { NConfigProvider, NMessageProvider } from 'naive-ui'
|
import { NConfigProvider, NMessageProvider } from "naive-ui";
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from "vue-router";
|
||||||
// definePageMeta({
|
|
||||||
// middleware: [
|
|
||||||
// function (to, from) {
|
|
||||||
// initFormData(to.query.type)
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// })
|
|
||||||
const route = useRoute()
|
|
||||||
const { type, id } = route.query
|
|
||||||
|
|
||||||
const formData = ref()
|
const route = useRoute();
|
||||||
|
const { type, id } = route.query;
|
||||||
|
|
||||||
|
const formData = ref();
|
||||||
|
|
||||||
async function initFormData() {
|
async function initFormData() {
|
||||||
if (type === 'add') {
|
if (type === "add") {
|
||||||
formData.value = {
|
formData.value = {
|
||||||
workFlow: {
|
workFlow: {
|
||||||
jurisdiction: 1, // auditStatus (0全部状态 1已发布-公开 2已发布-仅自己可见 3审核中 4审核未通过)
|
jurisdiction: 1, // auditStatus (0全部状态 1已发布-公开 2已发布-仅自己可见 3审核中 4审核未通过)
|
||||||
original: 0, // original 0原创 1转载
|
original: 0, // original 0原创 1转载
|
||||||
onlineUse: 0, // 是否允许在线使用(0允许 1不允许)
|
onlineUse: 0, // 是否允许在线使用(0允许 1不允许)
|
||||||
download: 0, // 是否允许下载工作流(0允许 1不允许)
|
download: 0, // 是否允许下载工作流(0允许 1不允许)
|
||||||
sell: 1, // 是否允许出售或商用(0允许 1不允许)
|
sell: 1, // 是否允许出售或商用(0允许 1不允许)
|
||||||
typeList: [],
|
typeList: [],
|
||||||
},
|
},
|
||||||
workFlowVersionList: [
|
workFlowVersionList: [
|
||||||
{
|
{
|
||||||
versionName: '',
|
versionName: "",
|
||||||
hideGenInfo: 0, // 是否隐藏图片生成信息
|
hideGenInfo: 0, // 是否隐藏图片生成信息
|
||||||
versionDescription: '', // 富文本
|
versionDescription: "", // 富文本
|
||||||
filePath: '', // 文件路径
|
filePath: "", // 文件路径
|
||||||
fileName: '', // 文件名
|
fileName: "", // 文件名
|
||||||
delFlag: '0', // 是否删除 0存在 2删除
|
delFlag: "0", // 是否删除 0存在 2删除
|
||||||
imagePaths: [],
|
imagePaths: [],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
};
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// type参数 1翻译
|
// type参数 1翻译
|
||||||
try {
|
try {
|
||||||
const res = await request.get(`/WorkFlow/selectWorkFlowVersionById?id=${id}`)
|
const res = await request.get(`/WorkFlow/selectWorkFlowVersionById?id=${id}`);
|
||||||
formData.value = res.data
|
formData.value = res.data;
|
||||||
if (formData.value.workFlow.type) {
|
if (formData.value.workFlow.type) {
|
||||||
formData.value.workFlow.typeList = formData.value.workFlow.type.split(',')
|
formData.value.workFlow.typeList = formData.value.workFlow.type.split(",");
|
||||||
}
|
} else {
|
||||||
else {
|
formData.value.workFlow.typeList = [];
|
||||||
formData.value.workFlow.typeList = []
|
|
||||||
}
|
}
|
||||||
if (formData.value.workFlow.activityParticipation) {
|
if (formData.value.workFlow.activityParticipation) {
|
||||||
formData.value.workFlow.activityParticipation = Number(formData.value.workFlow.activityParticipation)
|
formData.value.workFlow.activityParticipation = Number(
|
||||||
|
formData.value.workFlow.activityParticipation
|
||||||
|
);
|
||||||
}
|
}
|
||||||
for (let i = 0; i < formData.value.workFlowVersionList.length; i++) {
|
if (formData.value.workFlowVersionList.length > 0) {
|
||||||
if (formData.value.workFlowVersionList[i].imagePaths) {
|
for (let i = 0; i < formData.value.workFlowVersionList.length; i++) {
|
||||||
formData.value.workFlowVersionList[i].imagePaths = formData.value.workFlowVersionList[i].imagePaths.split(',')
|
if (formData.value.workFlowVersionList[i].imagePaths) {
|
||||||
|
formData.value.workFlowVersionList[
|
||||||
|
i
|
||||||
|
].imagePaths = formData.value.workFlowVersionList[i].imagePaths.split(",");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// else {
|
} catch (error) {
|
||||||
// formData.value.workFlow.activityParticipationList = []
|
console.log(error);
|
||||||
// }
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
console.log(error)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
initFormData()
|
initFormData();
|
||||||
const EditVersionRef = ref()
|
const EditVersionRef = ref();
|
||||||
const currentStep = ref(1)
|
const currentStep = ref(1);
|
||||||
|
|
||||||
function handleAddVersion() {
|
function handleAddVersion() {
|
||||||
EditVersionRef.value.addVersion()
|
EditVersionRef.value.addVersion();
|
||||||
}
|
}
|
||||||
function nextStep() {
|
function nextStep() {
|
||||||
currentStep.value += 1
|
currentStep.value += 1;
|
||||||
}
|
}
|
||||||
function preStep() {
|
function preStep() {
|
||||||
currentStep.value -= 1
|
currentStep.value -= 1;
|
||||||
}
|
}
|
||||||
const timeLineList = ref([
|
const timeLineList = ref([
|
||||||
{
|
{
|
||||||
name: '创建模版',
|
name: "编辑工作流",
|
||||||
index: 1,
|
index: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '编辑版本',
|
name: "编辑版本",
|
||||||
index: 2,
|
index: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '上传图片',
|
name: "上传图片",
|
||||||
index: 3,
|
index: 3,
|
||||||
},
|
},
|
||||||
])
|
]);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -106,7 +100,11 @@ const timeLineList = ref([
|
||||||
<div class="w-[60%]">
|
<div class="w-[60%]">
|
||||||
<TimeLine :time-line-list="timeLineList" :current-step="currentStep" />
|
<TimeLine :time-line-list="timeLineList" :current-step="currentStep" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="currentStep === 2" class="cursor-pointer flex items-center justify-center rounded-full border border-solid border-[#000] px-4 py-1" @click="handleAddVersion">
|
<div
|
||||||
|
v-if="currentStep === 2"
|
||||||
|
class="cursor-pointer flex items-center justify-center rounded-full border border-solid border-[#000] px-4 py-1"
|
||||||
|
@click="handleAddVersion"
|
||||||
|
>
|
||||||
<CirclePlus class="mr-2" />
|
<CirclePlus class="mr-2" />
|
||||||
添加版本
|
添加版本
|
||||||
</div>
|
</div>
|
||||||
|
@ -118,7 +116,12 @@ const timeLineList = ref([
|
||||||
<EditWorkFlow v-model="formData" @next-step="nextStep" />
|
<EditWorkFlow v-model="formData" @next-step="nextStep" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="currentStep === 2" class="second-step">
|
<div v-if="currentStep === 2" class="second-step">
|
||||||
<EditVersion ref="EditVersionRef" v-model="formData" @pre-step="preStep" @next-step="nextStep" />
|
<EditVersion
|
||||||
|
ref="EditVersionRef"
|
||||||
|
v-model="formData"
|
||||||
|
@pre-step="preStep"
|
||||||
|
@next-step="nextStep"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="currentStep === 3" class="third-step">
|
<div v-if="currentStep === 3" class="third-step">
|
||||||
|
|
|
@ -0,0 +1,188 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { commonApi } from "@/api/common";
|
||||||
|
import { ArrowBack, ArrowForward } from "@vicons/ionicons5";
|
||||||
|
import { NConfigProvider, NMessageProvider } from "naive-ui";
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
layout: "default",
|
||||||
|
});
|
||||||
|
const params = ref({
|
||||||
|
name: null,
|
||||||
|
order: null,
|
||||||
|
type: null,
|
||||||
|
});
|
||||||
|
const listRef = ref(null);
|
||||||
|
// function handleSearch() {
|
||||||
|
// onSearch()
|
||||||
|
// }
|
||||||
|
const workFlowCategoryList = ref([]);
|
||||||
|
async function getDictType() {
|
||||||
|
try {
|
||||||
|
const res = await commonApi.dictType({ type: "work_flow_type_child" });
|
||||||
|
if (res.code === 200) {
|
||||||
|
workFlowCategoryList.value = res.data;
|
||||||
|
workFlowCategoryList.value.unshift({
|
||||||
|
dictValue: null,
|
||||||
|
dictLabel: "全部",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getDictType();
|
||||||
|
|
||||||
|
//执行搜索的时候
|
||||||
|
function onSearch() {
|
||||||
|
if (listRef.value) {
|
||||||
|
listRef.value?.initPageNUm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 切换类型
|
||||||
|
function changeCategory(item: any) {
|
||||||
|
params.value.type = item.dictValue;
|
||||||
|
onSearch();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="p-6">
|
||||||
|
<div class="w-full">
|
||||||
|
<n-carousel show-arrow autoplay>
|
||||||
|
<img
|
||||||
|
class="carousel-img"
|
||||||
|
src="https://naive-ui.oss-cn-beijing.aliyuncs.com/carousel-img/carousel1.jpeg"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
class="carousel-img"
|
||||||
|
src="https://naive-ui.oss-cn-beijing.aliyuncs.com/carousel-img/carousel2.jpeg"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
class="carousel-img"
|
||||||
|
src="https://naive-ui.oss-cn-beijing.aliyuncs.com/carousel-img/carousel3.jpeg"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
class="carousel-img"
|
||||||
|
src="https://naive-ui.oss-cn-beijing.aliyuncs.com/carousel-img/carousel4.jpeg"
|
||||||
|
/>
|
||||||
|
<template #arrow="{ prev, next }">
|
||||||
|
<div class="custom-arrow">
|
||||||
|
<button type="button" class="custom-arrow--left" @click="prev">
|
||||||
|
<n-icon><ArrowBack /></n-icon>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="custom-arrow--right" @click="next">
|
||||||
|
<n-icon><ArrowForward /></n-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #dots="{ total, currentIndex, to }">
|
||||||
|
<ul class="custom-dots">
|
||||||
|
<li
|
||||||
|
v-for="index of total"
|
||||||
|
:key="index"
|
||||||
|
:class="{ ['is-active']: currentIndex === index - 1 }"
|
||||||
|
@click="to(index - 1)"
|
||||||
|
/>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
</n-carousel>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center mt-4">
|
||||||
|
<div class="text-[20px] mr-4">工作流</div>
|
||||||
|
<HeaderSearchInput v-model="params.name" @search="onSearch" />
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-wrap mt-2">
|
||||||
|
<div class="flex flex-wrap">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in workFlowCategoryList"
|
||||||
|
:key="index"
|
||||||
|
:style="{
|
||||||
|
color: item.dictValue === params.type ? '#fff' : '#000',
|
||||||
|
background: item.dictValue === params.type ? '#000' : '#fff',
|
||||||
|
}"
|
||||||
|
@click="changeCategory(item)"
|
||||||
|
class="px-2 py-1 rounded-full mr-4 cursor-pointer"
|
||||||
|
>
|
||||||
|
{{ item.dictLabel }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-wrap">
|
||||||
|
<NConfigProvider>
|
||||||
|
<NMessageProvider>
|
||||||
|
<WorkFlowList ref="listRef" :params="params" />
|
||||||
|
</NMessageProvider>
|
||||||
|
</NConfigProvider>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.grid > div {
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
.grid > div:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
.carousel-img {
|
||||||
|
width: 100%;
|
||||||
|
height: 180px;
|
||||||
|
border-radius: 10px;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-arrow {
|
||||||
|
display: flex;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 25px;
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-arrow button {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
margin-right: 12px;
|
||||||
|
color: #fff;
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
border-width: 0;
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-arrow button:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-arrow button:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
transform-origin: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-dots {
|
||||||
|
display: flex;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 20px;
|
||||||
|
left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-dots li {
|
||||||
|
display: inline-block;
|
||||||
|
width: 12px;
|
||||||
|
height: 4px;
|
||||||
|
margin: 0 3px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: rgba(255, 255, 255, 0.4);
|
||||||
|
transition: width 0.3s, background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-dots li.is-active {
|
||||||
|
width: 40px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -2,11 +2,9 @@
|
||||||
import { Heart, PersonAddOutline } from '@vicons/ionicons5'
|
import { Heart, PersonAddOutline } from '@vicons/ionicons5'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CircleUser,
|
CircleUser,
|
||||||
Download,
|
Download,
|
||||||
EllipsisVertical,
|
EllipsisVertical, Play
|
||||||
HardDriveUpload,
|
|
||||||
Play,
|
|
||||||
} from 'lucide-vue-next'
|
} from 'lucide-vue-next'
|
||||||
import { NConfigProvider, NMessageProvider } from 'naive-ui'
|
import { NConfigProvider, NMessageProvider } from 'naive-ui'
|
||||||
import { nextTick, ref } from 'vue'
|
import { nextTick, ref } from 'vue'
|
||||||
|
@ -64,6 +62,7 @@ async function getInfo() {
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
}
|
}
|
||||||
|
getAttention()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
|
@ -89,7 +88,7 @@ const selectUserInfo = ref<SelectUserInfo>({
|
||||||
// 获取点赞粉丝等的数量
|
// 获取点赞粉丝等的数量
|
||||||
async function getAttention() {
|
async function getAttention() {
|
||||||
try {
|
try {
|
||||||
const res = await request.get('/attention/selectUserInfo')
|
const res = await request.get(`/attention/selectUserInfo?userId=${detailsInfo.value.userId}`)
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
selectUserInfo.value = res.data
|
selectUserInfo.value = res.data
|
||||||
}
|
}
|
||||||
|
@ -98,7 +97,6 @@ async function getAttention() {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getAttention()
|
|
||||||
|
|
||||||
// 举报/编辑/删除
|
// 举报/编辑/删除
|
||||||
const isDelete = ref(false)
|
const isDelete = ref(false)
|
||||||
|
@ -361,21 +359,21 @@ async function onLike() {
|
||||||
class="h-[12px] w-[12px] text-black menu-icon m-1 text-[#969798] m-0"
|
class="h-[12px] w-[12px] text-black menu-icon m-1 text-[#969798] m-0"
|
||||||
/>
|
/>
|
||||||
<span class="mr-2"> {{ selectUserInfo.bean }} </span>
|
<span class="mr-2"> {{ selectUserInfo.bean }} </span>
|
||||||
<component
|
<!-- <component
|
||||||
:is="HardDriveUpload"
|
:is="HardDriveUpload"
|
||||||
class="h-[12px] w-[14px] text-black menu-icon m-1 text-[#969798]"
|
class="h-[12px] w-[14px] text-black menu-icon m-1 text-[#969798]"
|
||||||
/>
|
/>
|
||||||
<span class="mr-2"> 2 </span>
|
<span class="mr-2"> 2 </span> -->
|
||||||
<component
|
<component
|
||||||
:is="Play"
|
:is="Play"
|
||||||
class="h-[12px] w-[12px] text-black menu-icon m-1 text-[#969798]"
|
class="h-[12px] w-[12px] text-black menu-icon m-1 text-[#969798]"
|
||||||
/>
|
/>
|
||||||
<span class="mr-2"> {{ detailsInfo.useNumber }} </span>
|
<span class="mr-2"> {{ selectUserInfo.modelRunNum }} </span>
|
||||||
<component
|
<component
|
||||||
:is="Download"
|
:is="Download"
|
||||||
class="h-[12px] w-[12px] text-black menu-icon m-1 text-[#969798]"
|
class="h-[12px] w-[12px] text-black menu-icon m-1 text-[#969798]"
|
||||||
/>
|
/>
|
||||||
<span class="mr-2"> {{ detailsInfo.downloadNumber }} </span>
|
<span class="mr-2"> {{ selectUserInfo.modelDownloadNum }} </span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -411,7 +409,7 @@ async function onLike() {
|
||||||
:mask-closable="false"
|
:mask-closable="false"
|
||||||
preset="dialog"
|
preset="dialog"
|
||||||
title="提示!"
|
title="提示!"
|
||||||
content="确定要将模型删除? 模型删除后无法找回"
|
content="确定要将工作流删除? 工作流删除后无法找回"
|
||||||
negative-text="取消"
|
negative-text="取消"
|
||||||
positive-text="确认"
|
positive-text="确认"
|
||||||
@negative-click="onDelete"
|
@negative-click="onDelete"
|
||||||
|
|
|
@ -4,8 +4,10 @@ export const useMenuStore = defineStore('menu', () => {
|
||||||
const activeMenu = ref('/model-square')
|
const activeMenu = ref('/model-square')
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
{ path: '/model-square', icon: 'i-carbon-gallery', label: '模型广场' },
|
{ path: '/model-square', icon: 'i-carbon-gallery', label: '模型广场' },
|
||||||
|
{ path: '/picture-square', icon: 'i-carbon-light-filled', label: '作品灵感' },
|
||||||
// { path: '/works-inspire', icon: 'i-carbon-light-filled', label: '作品灵感' },
|
// { path: '/works-inspire', icon: 'i-carbon-light-filled', label: '作品灵感' },
|
||||||
// { path: '/workspace', icon: 'i-carbon-workspace', label: '工作台' },
|
// { path: '/workspace', icon: 'i-carbon-workspace', label: '工作流' },
|
||||||
|
{ path: '/work-square', icon: 'i-carbon-workspace', label: '工作流' },
|
||||||
// { path: '/web-ui', icon: 'i-carbon-image-search', label: '在线生图' },
|
// { path: '/web-ui', icon: 'i-carbon-image-search', label: '在线生图' },
|
||||||
// { path: '/comfy-ui', icon: 'i-carbon-image-search', label: '在线工作流' },
|
// { path: '/comfy-ui', icon: 'i-carbon-image-search', label: '在线工作流' },
|
||||||
// { path: '/training-lora', icon: 'i-carbon-machine-learning', label: '训练LoRA' },
|
// { path: '/training-lora', icon: 'i-carbon-machine-learning', label: '训练LoRA' },
|
||||||
|
|
|
@ -100,7 +100,7 @@ class RequestHttp {
|
||||||
message.error('请求参数错误')
|
message.error('请求参数错误')
|
||||||
break
|
break
|
||||||
case 401:
|
case 401:
|
||||||
message.error('未登录或登录已过期')
|
// message.error('未登录或登录已过期')
|
||||||
break
|
break
|
||||||
case 403:
|
case 403:
|
||||||
message.error('没有权限')
|
message.error('没有权限')
|
||||||
|
@ -108,9 +108,9 @@ class RequestHttp {
|
||||||
case 404:
|
case 404:
|
||||||
message.error('请求的资源不存在')
|
message.error('请求的资源不存在')
|
||||||
break
|
break
|
||||||
case 500:
|
// case 500:
|
||||||
message.error('服务器错误')
|
// message.error('服务器错误')
|
||||||
break
|
// break
|
||||||
default:
|
default:
|
||||||
message.error('未知错误')
|
message.error('未知错误')
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,8 +84,8 @@ export default defineNuxtConfig({
|
||||||
devProxy: {
|
devProxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
// target: 'http://1.13.246.108:8080', // 线上
|
// target: 'http://1.13.246.108:8080', // 线上
|
||||||
target: 'http://192.168.2.10:8080', // 代
|
// target: 'http://192.168.2.10:8080', // 代
|
||||||
// target: 'http://192.168.1.69:8080', // 嗨
|
target: 'http://192.168.1.69:8080', // 嗨
|
||||||
// target: 'https://2d1a399f.r27.cpolar.top', // 嗨
|
// target: 'https://2d1a399f.r27.cpolar.top', // 嗨
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
prependPath: true,
|
prependPath: true,
|
||||||
|
|
Loading…
Reference in New Issue