update
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
"crypto-js": "^4.2.0",
|
||||
"pinia": "^3.0.2",
|
||||
"pinia-plugin-persistedstate": "^4.2.0",
|
||||
"plyr": "^3.7.8",
|
||||
"tinymce": "^7.8.0",
|
||||
"uqrcodejs": "^4.0.7",
|
||||
"uuid": "^11.1.0",
|
||||
|
||||
34
pnpm-lock.yaml
generated
34
pnpm-lock.yaml
generated
@@ -23,6 +23,9 @@ importers:
|
||||
pinia-plugin-persistedstate:
|
||||
specifier: ^4.2.0
|
||||
version: 4.2.0(pinia@3.0.2(vue@3.5.13))
|
||||
plyr:
|
||||
specifier: ^3.7.8
|
||||
version: 3.7.8
|
||||
tinymce:
|
||||
specifier: ^7.8.0
|
||||
version: 7.8.0
|
||||
@@ -1412,6 +1415,9 @@ packages:
|
||||
csstype@3.1.3:
|
||||
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
|
||||
|
||||
custom-event-polyfill@1.0.7:
|
||||
resolution: {integrity: sha512-TDDkd5DkaZxZFM8p+1I3yAlvM3rSr1wbrOliG4yJiwinMZN8z/iGL7BTlDkrJcYTmgUSb4ywVCc3ZaUtOtC76w==}
|
||||
|
||||
dayjs@1.11.13:
|
||||
resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
|
||||
|
||||
@@ -1895,6 +1901,9 @@ packages:
|
||||
lines-and-columns@1.2.4:
|
||||
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
||||
|
||||
loadjs@4.3.0:
|
||||
resolution: {integrity: sha512-vNX4ZZLJBeDEOBvdr2v/F+0aN5oMuPu7JTqrMwp+DtgK+AryOlpy6Xtm2/HpNr+azEa828oQjOtWsB6iDtSfSQ==}
|
||||
|
||||
local-pkg@1.1.1:
|
||||
resolution: {integrity: sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==}
|
||||
engines: {node: '>=14'}
|
||||
@@ -2121,6 +2130,9 @@ packages:
|
||||
pkg-types@2.1.0:
|
||||
resolution: {integrity: sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A==}
|
||||
|
||||
plyr@3.7.8:
|
||||
resolution: {integrity: sha512-yG/EHDobwbB/uP+4Bm6eUpJ93f8xxHjjk2dYcD1Oqpe1EcuQl5tzzw9Oq+uVAzd2lkM11qZfydSiyIpiB8pgdA==}
|
||||
|
||||
possible-typed-array-names@1.1.0:
|
||||
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -2187,6 +2199,9 @@ packages:
|
||||
queue-microtask@1.2.3:
|
||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||
|
||||
rangetouch@2.0.1:
|
||||
resolution: {integrity: sha512-sln+pNSc8NGaHoLzwNBssFSf/rSYkqeBXzX1AtJlkJiUaVSJSbRAWJk+4omsXkN+EJalzkZhWQ3th1m0FpR5xA==}
|
||||
|
||||
rc9@2.1.2:
|
||||
resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==}
|
||||
|
||||
@@ -2519,6 +2534,9 @@ packages:
|
||||
uqrcodejs@4.0.7:
|
||||
resolution: {integrity: sha512-84+aZmD2godCVI+93lxE3YUAPNY8zAJvNA7xRS7R7U+q57KzMDepBSfNCwoRUhWOfR6eHFoAOcHRPwsP6ka1cA==}
|
||||
|
||||
url-polyfill@1.1.13:
|
||||
resolution: {integrity: sha512-tXzkojrv2SujumYthZ/WjF7jaSfNhSXlYMpE5AYdL2I3D7DCeo+mch8KtW2rUuKjDg+3VXODXHVgipt8yGY/eQ==}
|
||||
|
||||
util-deprecate@1.0.2:
|
||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||
|
||||
@@ -4095,6 +4113,8 @@ snapshots:
|
||||
|
||||
csstype@3.1.3: {}
|
||||
|
||||
custom-event-polyfill@1.0.7: {}
|
||||
|
||||
dayjs@1.11.13: {}
|
||||
|
||||
debug@4.4.0:
|
||||
@@ -4560,6 +4580,8 @@ snapshots:
|
||||
|
||||
lines-and-columns@1.2.4: {}
|
||||
|
||||
loadjs@4.3.0: {}
|
||||
|
||||
local-pkg@1.1.1:
|
||||
dependencies:
|
||||
mlly: 1.7.4
|
||||
@@ -4764,6 +4786,14 @@ snapshots:
|
||||
exsolve: 1.0.5
|
||||
pathe: 2.0.3
|
||||
|
||||
plyr@3.7.8:
|
||||
dependencies:
|
||||
core-js: 3.42.0
|
||||
custom-event-polyfill: 1.0.7
|
||||
loadjs: 4.3.0
|
||||
rangetouch: 2.0.1
|
||||
url-polyfill: 1.1.13
|
||||
|
||||
possible-typed-array-names@1.1.0: {}
|
||||
|
||||
postcss-import@15.1.0(postcss@8.5.3):
|
||||
@@ -4817,6 +4847,8 @@ snapshots:
|
||||
|
||||
queue-microtask@1.2.3: {}
|
||||
|
||||
rangetouch@2.0.1: {}
|
||||
|
||||
rc9@2.1.2:
|
||||
dependencies:
|
||||
defu: 6.1.4
|
||||
@@ -5199,6 +5231,8 @@ snapshots:
|
||||
|
||||
uqrcodejs@4.0.7: {}
|
||||
|
||||
url-polyfill@1.1.13: {}
|
||||
|
||||
util-deprecate@1.0.2: {}
|
||||
|
||||
util@0.12.5:
|
||||
|
||||
@@ -232,6 +232,69 @@ const merchant = {
|
||||
data: {id}
|
||||
});
|
||||
},
|
||||
addChildrenComment: async (data) => {
|
||||
return request({
|
||||
url: '/index/task/addChildrenComment',
|
||||
method: Method.POST,
|
||||
data: data
|
||||
});
|
||||
},
|
||||
getChildrenComment: async (id) => {
|
||||
return request({
|
||||
url: '/index/task/getChildrenComment',
|
||||
method: Method.POST,
|
||||
data: {id}
|
||||
});
|
||||
},
|
||||
delChildrenComment: async (id) => {
|
||||
return request({
|
||||
url: '/index/task/delChildrenComment',
|
||||
method: Method.POST,
|
||||
data: {id}
|
||||
});
|
||||
},
|
||||
editChildrenMaterial: async (data) => {
|
||||
return request({
|
||||
url: '/index/task/editChildrenMaterial',
|
||||
method: Method.POST,
|
||||
data: data
|
||||
});
|
||||
},
|
||||
editChildrenMaterimal: async (data) => {
|
||||
return request({
|
||||
url: '/index/task/editChildrenMaterimal',
|
||||
method: Method.POST,
|
||||
data: data
|
||||
});
|
||||
},
|
||||
startTask: async (data) => {
|
||||
return request({
|
||||
url: '/index/task/startTask',
|
||||
method: Method.POST,
|
||||
data: data
|
||||
});
|
||||
},
|
||||
moneyToTask: async (id) => {
|
||||
return request({
|
||||
url: '/index/task/moneyToTask',
|
||||
method: Method.POST,
|
||||
data: {id}
|
||||
});
|
||||
},
|
||||
stopTask: async (id) => {
|
||||
return request({
|
||||
url: '/index/task/stopTask',
|
||||
method: Method.POST,
|
||||
data: {id}
|
||||
});
|
||||
},
|
||||
endTask: async (id) => {
|
||||
return request({
|
||||
url: '/index/task/endTask',
|
||||
method: Method.POST,
|
||||
data: {id}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default merchant;
|
||||
|
||||
BIN
src/assets/images/back.png
Normal file
BIN
src/assets/images/back.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 551 B |
46
src/components/Comment/index.vue
Normal file
46
src/components/Comment/index.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<script setup>
|
||||
import Api from "../../api/index.js";
|
||||
import {Message} from "@arco-design/web-vue";
|
||||
|
||||
const emits = defineEmits(['success']);
|
||||
const {data, hideDelete} = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: {}
|
||||
},
|
||||
hideDelete: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
}
|
||||
});
|
||||
const del = async (id) => {
|
||||
const {msg} = await Api.merchant.delChildrenComment(id);
|
||||
Message.success(msg);
|
||||
emits('success');
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-for="(item, index) in data" :key="item.id" class="flex items-center">
|
||||
<div class="mr-[5px] min-w-[15px]">{{ index + 1 }}.</div>
|
||||
<div class="box">
|
||||
<div class="flex items-center whitespace-nowrap" v-if="item.pid>0">
|
||||
<img class="size-[9px] mr-[5px]" src="../../assets/images/back.png" alt=""/>
|
||||
回复{{ data.findIndex(k => k.pid === item.pid) }}:
|
||||
</div>
|
||||
<img class="size-[32px]" v-if="item.image" :src="item.image" alt=""/>
|
||||
<div class="whitespace-nowrap">{{ item.intro }}</div>
|
||||
</div>
|
||||
<a-popconfirm content="确认删除吗?" @ok="del(item.id)" v-if="!hideDelete">
|
||||
<a-button size="mini" shape="circle" type="text">
|
||||
<icon-delete/>
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.box {
|
||||
@apply p-[4px] bg-[#F2F3F5] flex items-center gap-[10px] pr-[15px] mr-[10px] min-h-[40px] flex-grow;
|
||||
}
|
||||
</style>
|
||||
@@ -16,11 +16,11 @@ const UserStore = useUserStore();
|
||||
<a-dropdown>
|
||||
<a-link
|
||||
:hoverable="false">
|
||||
{{ UserStore.userInfo.nickname || UserStore.userInfo.mobile }}
|
||||
{{ UserStore?.userInfo?.nickname || UserStore?.userInfo?.mobile }}
|
||||
<icon-down/>
|
||||
</a-link>
|
||||
<template #content>
|
||||
<a-doption @click="UserStore.logout">退出登陆</a-doption>
|
||||
<a-doption @click="UserStore?.logout">退出登陆</a-doption>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup>
|
||||
import {reactive, ref} from "vue";
|
||||
import {Message} from "@arco-design/web-vue";
|
||||
import Api from "../../../api/index.js";
|
||||
import Api from "../../api/index.ts";
|
||||
|
||||
const {money} = defineProps({
|
||||
money: {
|
||||
89
src/components/Pay/PayTask.vue
Normal file
89
src/components/Pay/PayTask.vue
Normal file
@@ -0,0 +1,89 @@
|
||||
<script setup>
|
||||
import {ref} from "vue";
|
||||
|
||||
const payInfo = ref(null);
|
||||
const visible = ref(false);
|
||||
const emits = defineEmits(['close']);
|
||||
let successFun = () => {
|
||||
};
|
||||
const open = (options) => {
|
||||
const {props, success} = options;
|
||||
successFun = success;
|
||||
console.log('?????', options)
|
||||
payInfo.value = props.payInfo;
|
||||
visible.value = true;
|
||||
}
|
||||
const close = () => {
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
const success = () => {
|
||||
successFun && successFun.apply();
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open,
|
||||
close
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-modal
|
||||
@ok="success"
|
||||
ok-text="确认支付"
|
||||
:ok-button-props="{
|
||||
disabled: payInfo?.user_money < payInfo?.total_money
|
||||
}"
|
||||
:width="600"
|
||||
id="PayTask-Modal"
|
||||
title-align="start"
|
||||
title="开始任务"
|
||||
v-model:visible="visible">
|
||||
<a-alert>平台提示:该款项不会直接打给达人,只有您对子任务确认结算后,才会打款给达人</a-alert>
|
||||
<div class="px-[30px] py-[16px] flex flex-col gap-[8px] justify-start items-start" v-if="payInfo">
|
||||
<div class="text-[#4E5969]">支付详情</div>
|
||||
<div class="flex">
|
||||
<div class="w-[100px]">任务金额(元):</div>
|
||||
<span class="text-[#4E5969]">¥{{ (payInfo.total_money - payInfo.serve_money).toFixed(2) }}</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div class="w-[100px]">服务费(元):</div>
|
||||
<span class="text-[#4E5969]">¥{{ payInfo.serve_money.toFixed(2) }}</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div class="w-[100px]">合计(元):</div>
|
||||
<span class="text-[#4E5969]">¥{{ payInfo.total_money.toFixed(2) }}</span>
|
||||
</div>
|
||||
<div class="text-[12px] text-[#86909C]">
|
||||
提示:①服务费按照实际消耗任务金额的百分比进行收取<br/>
|
||||
②若任务未被领取或者未支付全部金额,则只收取实际支付部分的服务费<br/>
|
||||
③剩余任务金额和服务费将退回钱包
|
||||
</div>
|
||||
<div>支付方式</div>
|
||||
<div class="mt-[5px]">
|
||||
<a-radio
|
||||
:disabled="payInfo.user_money < payInfo?.total_money"
|
||||
:default-checked="payInfo.user_money > payInfo?.total_money">
|
||||
钱包余额
|
||||
(可用¥{{ payInfo?.user_money?.toFixed(2) }})
|
||||
</a-radio>
|
||||
</div>
|
||||
<div class="text-[12px] text-[#86909C] pl-[20px]">
|
||||
*余额不足本次任务所需,请充值后再进行支付
|
||||
</div>
|
||||
<a-button class="mt-[20px]" type="primary">去充值</a-button>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
#PayTask-Modal {
|
||||
.arco-modal-body {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
30
src/components/Plyr/Plyr.vue
Normal file
30
src/components/Plyr/Plyr.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<script setup>
|
||||
import {onMounted, ref} from "vue";
|
||||
import Plyr from "plyr";
|
||||
import 'plyr/dist/plyr.css';
|
||||
|
||||
const {src} = defineProps({
|
||||
src: {
|
||||
type: String,
|
||||
default: "",
|
||||
}
|
||||
});
|
||||
const plyrContainer = ref();
|
||||
onMounted(() => {
|
||||
new Plyr(plyrContainer.value, {});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="plyrContainer">
|
||||
<video ref="plyrContainer">
|
||||
<source :src="src">
|
||||
</video>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.plyrContainer {
|
||||
width: 70%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +1,9 @@
|
||||
<script setup>
|
||||
import XImage from "../../components/XImage/Index.vue";
|
||||
import {reactive, ref, watch} from 'vue';
|
||||
import Api from "../../api/index.ts";
|
||||
import {Message} from "@arco-design/web-vue";
|
||||
import Comment from "../Comment/index.vue";
|
||||
|
||||
const emits = defineEmits(['success']);
|
||||
const {id} = defineProps({
|
||||
@@ -12,7 +14,7 @@ const {id} = defineProps({
|
||||
});
|
||||
const visible = ref(false);
|
||||
const detail = reactive({});
|
||||
const activeKey = ref(1);
|
||||
const activeKey = ref(0);
|
||||
|
||||
watch(
|
||||
() => visible.value,
|
||||
@@ -28,12 +30,14 @@ watch(
|
||||
const passTaskChildren = async () => {
|
||||
const {code, msg} = await Api.admin.passTaskChildren(id);
|
||||
if (code === 1) Message.success(msg);
|
||||
visible.value = false;
|
||||
emits('success');
|
||||
}
|
||||
|
||||
const refuseTaskChildren = async () => {
|
||||
const {code, msg} = await Api.admin.refuseTaskChildren(id);
|
||||
if (code === 1) Message.success(msg);
|
||||
visible.value = false;
|
||||
emits('success');
|
||||
}
|
||||
</script>
|
||||
@@ -64,41 +68,37 @@ const refuseTaskChildren = async () => {
|
||||
</a-textarea>
|
||||
</a-form-item>
|
||||
<a-form-item label="话题">
|
||||
<div id="tag-list" class="w-full bg-[var(--color-neutral-2)] p-[4px]">
|
||||
<div v-if="item.tags_arr.length > 0" id="tag-list"
|
||||
class="w-full bg-[var(--color-neutral-2)] p-[4px]">
|
||||
<a-tag v-for="v in item.tags_arr">#{{ v }}</a-tag>
|
||||
</div>
|
||||
<div v-else>
|
||||
暂无话题
|
||||
</div>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="素材">
|
||||
<div class="flex flex-wrap gap-[16px]">
|
||||
<a-image
|
||||
v-for="v in item.materia_arr"
|
||||
<div v-if="item.materia_arr.length > 0" class="flex flex-wrap gap-[16px]">
|
||||
<x-image
|
||||
v-for="(v, index) in item.materia_arr"
|
||||
:hide-delete="true"
|
||||
:key="index"
|
||||
width="60px"
|
||||
height="60px"
|
||||
:src="v">
|
||||
</a-image>
|
||||
</x-image>
|
||||
</div>
|
||||
<div v-else>
|
||||
暂无话题
|
||||
</div>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="评论区内容">
|
||||
<div class="flex flex-col gap-[8px] w-full">
|
||||
<div class="w-full flex gap-[10px] items-center" v-for="v in detail.comment">
|
||||
{{ v.id }}.
|
||||
<div class="flex-grow bg-[var(--color-neutral-2)] p-[4px] h-[40px] flex items-center">
|
||||
<div class="text-[#4E5969] text-[14px] mr-[4px]" v-if="v.pid !== 0">
|
||||
<icon-redo/>
|
||||
回复{{ v.pid }}:
|
||||
</div>
|
||||
<a-image
|
||||
v-if="v.image"
|
||||
class="mr-[12px]"
|
||||
width="30px"
|
||||
height="30px"
|
||||
:src="v.image">
|
||||
</a-image>
|
||||
第三个很好用,大家可以在网上搜同款
|
||||
</div>
|
||||
<div v-if="item.comment.length > 0" class="flex flex-col gap-[8px] w-full">
|
||||
<comment :data="item.comment" :hide-delete="true"></comment>
|
||||
</div>
|
||||
<div v-else>
|
||||
暂无评论区内容
|
||||
</div>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
@@ -107,14 +107,18 @@ const refuseTaskChildren = async () => {
|
||||
|
||||
<template #footer>
|
||||
<div class="flex items-center gap-[8px]">
|
||||
<a-checkbox-group>
|
||||
<template v-for="(item, index) in detail.content" :key="item">
|
||||
<a-checkbox v-show="activeKey === index" :value="item">选中</a-checkbox>
|
||||
</template>
|
||||
</a-checkbox-group>
|
||||
<!-- <a-checkbox-group>-->
|
||||
<!-- <template v-for="(item, index) in detail.content" :key="item">-->
|
||||
<!-- <a-checkbox v-show="activeKey === index" :value="item">选中</a-checkbox>-->
|
||||
<!-- </template>-->
|
||||
<!-- </a-checkbox-group>-->
|
||||
|
||||
<a-button @click="passTaskChildren" type="primary" class="ml-auto">通过</a-button>
|
||||
<template v-if="detail.check_status === 0">
|
||||
<a-button @click="passTaskChildren" type="primary" class="ml-auto">通过
|
||||
</a-button>
|
||||
<a-button @click="refuseTaskChildren">拒绝</a-button>
|
||||
</template>
|
||||
<a-button v-else @click="visible=false" class="ml-auto">关闭</a-button>
|
||||
</div>
|
||||
</template>
|
||||
</a-modal>
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<script setup>
|
||||
import {ref} from "vue";
|
||||
import {Modal} from "@arco-design/web-vue";
|
||||
import {determineMediaType} from "../../utils/index.js";
|
||||
import Plyr from "../Plyr/Plyr.vue";
|
||||
|
||||
const emits = defineEmits(['delete']);
|
||||
const {width, height, aspect} = defineProps({
|
||||
const {width, height, aspect, hideDelete, src} = defineProps({
|
||||
width: {
|
||||
type: String,
|
||||
default: '80px'
|
||||
@@ -15,6 +17,14 @@ const {width, height, aspect} = defineProps({
|
||||
aspect: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
hideDelete: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
src: {
|
||||
type: String,
|
||||
default: "",
|
||||
}
|
||||
});
|
||||
const previewVisible = ref(false);
|
||||
@@ -33,28 +43,61 @@ const deleteImage = () => {
|
||||
<template>
|
||||
<div class="x-image-box" :style="{aspectRatio: aspect ? '1 / 1' : ''}">
|
||||
<a-image
|
||||
v-if="determineMediaType(src) === 'Image'"
|
||||
fit="contain"
|
||||
v-model:preview-visible="previewVisible"
|
||||
@preview-visible-change="(v)=>previewVisible=v"
|
||||
v-bind="$attrs"
|
||||
:src="src"
|
||||
:width="width"
|
||||
:height="height">
|
||||
</a-image>
|
||||
<video class="video" v-else-if="determineMediaType(src) === 'Video'" :src="src">
|
||||
</video>
|
||||
|
||||
<transition name="fade">
|
||||
<div class="video-preview-mask" v-if="determineMediaType(src) === 'Video' && previewVisible"
|
||||
@click="previewVisible=false">
|
||||
<plyr :src="src" @click.stop></plyr>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<div class="mask">
|
||||
<icon-eye @click="previewVisible=true" class="icon" style="color: #fff"/>
|
||||
<icon-delete @click="deleteImage" class="icon" style="color: #fff"/>
|
||||
<icon-delete @click="deleteImage" v-if="!hideDelete" class="icon" style="color: #fff"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.5s;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.x-image-box {
|
||||
@apply cursor-pointer relative box-border rounded-[6px] overflow-hidden;
|
||||
outline: 1px solid #E5E6EB;
|
||||
width: v-bind(width);
|
||||
height: v-bind(height);
|
||||
|
||||
.video {
|
||||
width: v-bind(width);
|
||||
height: v-bind(height);
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.video-preview-mask {
|
||||
@apply size-full fixed z-[999] left-0 top-0 bg-[rgba(0,0,0,.6)] flex justify-center items-center;
|
||||
}
|
||||
|
||||
&:hover .mask {
|
||||
background-color: rgba(0, 0, 0, .4);
|
||||
|
||||
|
||||
44
src/components/XImage/XImageSmallList.vue
Normal file
44
src/components/XImage/XImageSmallList.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<script setup>
|
||||
const {list, size} = defineProps({
|
||||
list: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: "40px"
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="x-image-small-list">
|
||||
<a-image :width="size" :height="size" :src="list[0]" v-bind="$attrs"></a-image>
|
||||
<div class="x-image-small-list-mask" v-if="list.length > 1">
|
||||
+{{ list.length - 1 }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.x-image-small-list {
|
||||
width: v-bind(size);
|
||||
height: v-bind(size);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
border-radius: 6px;
|
||||
|
||||
.x-image-small-list-mask {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background-color: rgba(0, 0, 0, .3);
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
16
src/hooks/usePayTask.js
Normal file
16
src/hooks/usePayTask.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import {createApp} from "vue";
|
||||
|
||||
import PayTask from "../components/Pay/PayTask.vue";
|
||||
|
||||
export const usePayTask = () => {
|
||||
const container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
const app = createApp(PayTask);
|
||||
const vm = app.mount(container);
|
||||
|
||||
const {open} = vm;
|
||||
|
||||
return {
|
||||
open
|
||||
};
|
||||
}
|
||||
@@ -3,20 +3,38 @@ import XSelect from "../../../components/XSelect/index.vue";
|
||||
import {reactive, ref} from "vue";
|
||||
import UploadAvatar from "../../../components/upload/UploadAvatar.vue";
|
||||
import Api from "../../../api/index.js";
|
||||
import {Message} from "@arco-design/web-vue";
|
||||
|
||||
const {id} = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
const emits = defineEmits(['success']);
|
||||
const {item, material} = defineProps({
|
||||
item: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
material: {
|
||||
type: Object,
|
||||
default: null,
|
||||
}
|
||||
});
|
||||
const form = reactive({
|
||||
type: 0,
|
||||
files: []
|
||||
is_reply: 0,
|
||||
pid: null,
|
||||
files: [],
|
||||
intro: null,
|
||||
});
|
||||
const visible = ref(false);
|
||||
const success = async () => {
|
||||
|
||||
const {msg} = await Api.merchant.addChildrenComment({
|
||||
id: material.id,
|
||||
material_id: item.id,
|
||||
pid: form.pid,
|
||||
image: form.files,
|
||||
intro: form.intro,
|
||||
is_reply: form.is_reply,
|
||||
});
|
||||
Message.success(msg);
|
||||
visible.value = false;
|
||||
emits('success');
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -34,14 +52,18 @@ const success = async () => {
|
||||
v-model:visible="visible"
|
||||
@ok="success">
|
||||
<a-form layout="vertical">
|
||||
<a-form-item label="是否回复之前评论">
|
||||
<a-radio-group v-model:model-value="form.type">
|
||||
<a-form-item label="是否回复之前评论" v-if="item.comment.length !== 0">
|
||||
<a-radio-group v-model:model-value="form.is_reply">
|
||||
<a-radio :value="0">否</a-radio>
|
||||
<a-radio :value="1">是</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="需要回复的评论" v-if="form.type===1">
|
||||
<x-select api=""></x-select>
|
||||
<a-form-item label="需要回复的评论" v-if="form.is_reply===1">
|
||||
<x-select
|
||||
v-model:model-value="form.pid"
|
||||
:apiPo="item.id"
|
||||
:api="Api.merchant.getChildrenComment">
|
||||
</x-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="添加图片">
|
||||
<UploadAvatar
|
||||
@@ -50,7 +72,7 @@ const success = async () => {
|
||||
</UploadAvatar>
|
||||
</a-form-item>
|
||||
<a-form-item label="添加文字">
|
||||
<a-textarea placeholder="请输入评论内容"></a-textarea>
|
||||
<a-textarea v-model:model-value="form.intro" placeholder="请输入评论内容"></a-textarea>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
<script setup>
|
||||
import {reactive, ref} from "vue";
|
||||
import {reactive, ref, watch} from "vue";
|
||||
import XImage from "../../../components/XImage/Index.vue";
|
||||
import UploadButton from "../../../components/upload/UploadButton.vue";
|
||||
import Api from "../../../api/index.js";
|
||||
import {v4} from "uuid";
|
||||
import MaterialSource from "./MaterialSource.vue";
|
||||
import {Message} from "@arco-design/web-vue";
|
||||
|
||||
const MaterialSourceRef = ref();
|
||||
const visible = ref(false);
|
||||
const targetList = reactive([]);
|
||||
const {id} = defineProps({
|
||||
const emits = defineEmits(['success']);
|
||||
const {id, material} = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
material: {
|
||||
type: Object,
|
||||
default: null,
|
||||
}
|
||||
});
|
||||
|
||||
@@ -23,8 +29,33 @@ const uploadSuccess = (url) => {
|
||||
});
|
||||
}
|
||||
|
||||
const success = async () => {
|
||||
watch(
|
||||
() => visible.value,
|
||||
(val) => {
|
||||
if (val) {
|
||||
MaterialSourceRef.value.getMaterialList();
|
||||
targetList.length = 0;
|
||||
material.material_arr.forEach(v => uploadSuccess(v));
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const open = () => {
|
||||
visible.value = true;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open
|
||||
});
|
||||
|
||||
const success = async () => {
|
||||
const {msg} = await Api.merchant.editChildrenMaterial({
|
||||
id: material.id,
|
||||
image: targetList.map(v => v.image),
|
||||
});
|
||||
Message.success(msg);
|
||||
emits('success');
|
||||
MaterialSourceRef.value.handleImage();
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -44,7 +75,7 @@ const success = async () => {
|
||||
v-model:visible="visible"
|
||||
title="添加素材">
|
||||
<div class="target">
|
||||
<div class="text-[#1D2129] text-[14px]">已添加5个素材(长摁图片可拖动排序)</div>
|
||||
<div class="text-[#1D2129] text-[14px]">已添加{{ targetList.length }}个素材(长摁图片可拖动排序)</div>
|
||||
<div class="grid grid-cols-6 mt-[10px] gap-[16px]">
|
||||
<x-image
|
||||
@delete="targetList.splice(index,1)"
|
||||
|
||||
@@ -12,11 +12,14 @@ const {id} = defineProps({
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
Api.merchant.getMaterialList(id).then(({data}) => {
|
||||
const getMaterialList = async () => {
|
||||
const {data} = await Api.merchant.getMaterialList(id);
|
||||
list.length = 0;
|
||||
list.push(...data);
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getMaterialList();
|
||||
});
|
||||
const select = (v) => {
|
||||
const flag = selectList.value.findIndex(k => k.id === v.id);
|
||||
@@ -27,11 +30,18 @@ const select = (v) => {
|
||||
selectList.value.push(v);
|
||||
}
|
||||
}
|
||||
const handle = async () => {
|
||||
if (type === 0) {
|
||||
const handleImage = async () => {
|
||||
if (type.value === 0) {
|
||||
selectList.value.filter(v => typeof v.id === 'number').forEach(v => {
|
||||
Api.merchant.delMaterial(v.id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
defineExpose({
|
||||
getMaterialList,
|
||||
handleImage
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
54
src/pages/merchant/components/ViewMaterial.vue
Normal file
54
src/pages/merchant/components/ViewMaterial.vue
Normal file
@@ -0,0 +1,54 @@
|
||||
<script setup>
|
||||
import XImage from "../../../components/XImage/Index.vue";
|
||||
import {ref} from "vue";
|
||||
|
||||
const visible = ref(false);
|
||||
const emits = defineEmits(['edit']);
|
||||
const {id, material} = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
material: {
|
||||
type: Object,
|
||||
default: null,
|
||||
}
|
||||
});
|
||||
|
||||
const edit = () => {
|
||||
visible.value = false;
|
||||
emits('edit');
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-button v-if="!$slots.default" type="primary" @click="visible=true">
|
||||
添加素材
|
||||
</a-button>
|
||||
<div @click="visible=true" v-else class="cursor-pointer">
|
||||
<slot></slot>
|
||||
</div>
|
||||
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
title-align="start"
|
||||
title="素材">
|
||||
<div class="grid grid-cols-4">
|
||||
<div v-for="(v, index) in material.material_arr" :key="index"
|
||||
class="flex flex-col justify-center items-center gap-[10px]">
|
||||
<x-image :src="v" :hide-delete="true"></x-image>
|
||||
素材{{ index }}
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="flex gap-[12px] justify-end">
|
||||
<a-button @click="edit">编辑素材</a-button>
|
||||
<a-button @click="visible=false" type="primary">确定</a-button>
|
||||
</div>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
import {reactive} from "vue";
|
||||
import Alipay from "../../../components/Alipay.vue";
|
||||
import Alipay from "../../../../../components/Pay/Alipay.vue";
|
||||
|
||||
const form = reactive({
|
||||
money: null,
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
<script setup>
|
||||
import {reactive} from "vue";
|
||||
import Comment from "../../../../components/Comment/index.vue";
|
||||
import {reactive, ref} from "vue";
|
||||
import useTableQuery from "../../../../hooks/useTableQuery.js";
|
||||
import Api from "../../../../api/index.js";
|
||||
import {useRoute} from "vue-router";
|
||||
import MaterialLibrary from "../../components/MaterialLibrary.vue";
|
||||
import AddMaterial from "../../components/AddMaterial.vue";
|
||||
import AddComment from "../../components/AddComment.vue";
|
||||
import XImageSmallList from "../../../../components/XImage/XImageSmallList.vue";
|
||||
import ViewMaterial from "../../components/ViewMaterial.vue";
|
||||
import {Message} from "@arco-design/web-vue";
|
||||
|
||||
const route = useRoute();
|
||||
const AddMaterialRef = ref();
|
||||
const columns = reactive([
|
||||
{
|
||||
title: '子任务编号',
|
||||
@@ -49,13 +54,31 @@ const vo = reactive({
|
||||
pageSize: 9,
|
||||
});
|
||||
|
||||
const {loading, pagination} = useTableQuery({
|
||||
const {loading, pagination, fetchData} = useTableQuery({
|
||||
parameter: po,
|
||||
api: Api.merchant.getTaskChildrenList,
|
||||
callback: (data) => {
|
||||
Object.assign(vo, data);
|
||||
}
|
||||
});
|
||||
|
||||
const editMaterial = (index) => {
|
||||
AddMaterialRef.value[index].open();
|
||||
}
|
||||
|
||||
const success = async () => {
|
||||
const {msg} = await Api.merchant.editChildrenMaterimal({
|
||||
id: vo.rows[0]?.task_id,
|
||||
data: vo.rows.map(v => v.childrenMaterial.map(k => ({
|
||||
id: k.id,
|
||||
title: k.title,
|
||||
content: k.content,
|
||||
tags: k.tags,
|
||||
}))).flat()
|
||||
});
|
||||
Message.success(msg);
|
||||
await fetchData();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -63,16 +86,17 @@ const {loading, pagination} = useTableQuery({
|
||||
<a-card class="flex-grow text-[14px]">
|
||||
<div class="flex justify-between">
|
||||
<MaterialLibrary :id="po.id"></MaterialLibrary>
|
||||
<a-button type="primary">确认修改</a-button>
|
||||
<a-button type="primary" @click="success">确认修改</a-button>
|
||||
</div>
|
||||
|
||||
<a-table
|
||||
:data="vo.rows"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:scroll="{x: 'auto'}"
|
||||
class="w-full mt-[20px] flex-grow"
|
||||
@page-change="(e) => pagination.current = e"
|
||||
:pagination="pagination">
|
||||
:pagination="false">
|
||||
<template v-slot:title="{record}">
|
||||
<div class="flex flex-col gap-[12px]">
|
||||
<a-input
|
||||
@@ -100,28 +124,46 @@ const {loading, pagination} = useTableQuery({
|
||||
</a-input>
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:material="{record}">
|
||||
<template v-slot:material="{record, rowIndex}">
|
||||
<div class="flex flex-col gap-[12px]">
|
||||
<div class="flex gap-[12px]" v-for="v in record.childrenMaterial">
|
||||
<add-material
|
||||
v-for="v in record.childrenMaterial"
|
||||
:id="po.id">
|
||||
ref="AddMaterialRef"
|
||||
@success="fetchData"
|
||||
:id="po.id"
|
||||
:material="v">
|
||||
<div class="add-materials">
|
||||
<icon-plus/>
|
||||
<div>添加</div>
|
||||
</div>
|
||||
</add-material>
|
||||
<view-material
|
||||
@edit="editMaterial(rowIndex)"
|
||||
:material="v"
|
||||
:id="po.id">
|
||||
<x-image-small-list
|
||||
:preview="false"
|
||||
v-if="v.material_arr.length>0"
|
||||
:list="v.material_arr">
|
||||
</x-image-small-list>
|
||||
</view-material>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:pl="{record}">
|
||||
<div class="flex flex-col gap-[12px]">
|
||||
<div v-for="(v, index) in record.childrenMaterial" :key="index" class="flex gap-[12px]">
|
||||
<add-comment
|
||||
v-for="v in record.childrenMaterial"
|
||||
:id="v.id">
|
||||
@success="fetchData"
|
||||
:material="record"
|
||||
:item="v">
|
||||
<div class="add-materials">
|
||||
<icon-plus/>
|
||||
<div>添加</div>
|
||||
</div>
|
||||
</add-comment>
|
||||
<comment @success="fetchData" :data="v.comment"></comment>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
@@ -6,6 +6,7 @@ import useTableQuery from "../../../../hooks/useTableQuery.js";
|
||||
import Api from "../../../../api/index.js";
|
||||
import {toPath} from "../../../../utils/index.js";
|
||||
import {Message} from "@arco-design/web-vue";
|
||||
import {usePayTask} from "../../../../hooks/usePayTask.js";
|
||||
|
||||
const columns = [
|
||||
{
|
||||
@@ -100,13 +101,44 @@ const po = reactive({
|
||||
wd: null,
|
||||
});
|
||||
|
||||
const {loading, pagination, initFetchData} = useTableQuery({
|
||||
const {loading, pagination, initFetchData, fetchData} = useTableQuery({
|
||||
parameter: po,
|
||||
api: Api.merchant.getTaskList,
|
||||
callback: (data) => {
|
||||
Object.assign(vo, data);
|
||||
}
|
||||
});
|
||||
|
||||
const {open: openPayTask} = usePayTask();
|
||||
|
||||
const startTask = async (_value, record) => {
|
||||
const {status, id} = record;
|
||||
|
||||
const showMessageAndFetch = async (msg) => {
|
||||
Message.success(msg);
|
||||
await fetchData();
|
||||
};
|
||||
|
||||
if (status === -2 || status === 4) {
|
||||
const {msg} = await Api.merchant.endTask(id);
|
||||
await showMessageAndFetch(msg);
|
||||
} else {
|
||||
const {data} = await Api.merchant.startTask({id});
|
||||
openPayTask({
|
||||
props: {payInfo: data},
|
||||
success: async () => {
|
||||
const {msg} = await Api.merchant.moneyToTask(id);
|
||||
await showMessageAndFetch(msg);
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const stopTask = async (id) => {
|
||||
const {msg} = await Api.merchant.stopTask(id);
|
||||
Message.success(msg);
|
||||
await fetchData();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -150,7 +182,8 @@ const {loading, pagination, initFetchData} = useTableQuery({
|
||||
</template>
|
||||
<template v-slot:start="{record}">
|
||||
<a-switch
|
||||
:disabled="record.status !== -2 || record.status !== 3"
|
||||
@change="startTask($event, record)"
|
||||
:disabled="record.status !== -2 && record.status !== 3 && record.status !== 4"
|
||||
:model-value="record.status === 4">
|
||||
</a-switch>
|
||||
</template>
|
||||
@@ -170,7 +203,9 @@ const {loading, pagination, initFetchData} = useTableQuery({
|
||||
@click="record.status >= 2 ? toPath('/home/task-center/look-min-task', {id: record.id}) : Message.warning('审核未通过')">
|
||||
查看子任务
|
||||
</a-link>
|
||||
<a-popconfirm content="确认终止吗?" @ok="stopTask(record.id)">
|
||||
<a-link :hoverable="false" status="danger">终止</a-link>
|
||||
</a-popconfirm>
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:exp>
|
||||
|
||||
@@ -21,3 +21,18 @@ export const baseImage = (url) => {
|
||||
if (!url) url = '';
|
||||
return url.startsWith('http') ? url : import.meta.env.VITE_API_URL + url;
|
||||
}
|
||||
|
||||
export const determineMediaType = (url) => {
|
||||
const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.svg'];
|
||||
const videoExtensions = ['.mp4', '.mov', '.avi', '.mkv', '.webm'];
|
||||
|
||||
const extension = url.substring(url.lastIndexOf('.')).toLowerCase();
|
||||
|
||||
if (imageExtensions.includes(extension)) {
|
||||
return 'Image';
|
||||
} else if (videoExtensions.includes(extension)) {
|
||||
return 'Video';
|
||||
} else {
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user