初始化
This commit is contained in:
76
pages/[custom].vue
Normal file
76
pages/[custom].vue
Normal file
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<!-- Banner -->
|
||||
<Banner :layout="layout" />
|
||||
|
||||
<!-- 简单模式(常规单页面) -->
|
||||
<PageContainer :form="form" :layout="layout" />
|
||||
|
||||
<!-- 高级模式(自定义组件) -->
|
||||
<div class="flex flex-col" v-if="layout?.showLayout">
|
||||
{{ layout }}
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useConfigInfo, useForm, useToken, useWebsite} from "~/composables/configState";
|
||||
import type {BreadcrumbItem} from "~/types/global";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import {getIdBySpm} from "~/utils/common";
|
||||
import PageContainer from "~/components/PageContainer.vue";
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
const website = useWebsite();
|
||||
const layout = ref<any>();
|
||||
const config = useConfigInfo();
|
||||
const token = useToken();
|
||||
const form = useForm();
|
||||
const breadcrumb = ref<BreadcrumbItem>();
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
|
||||
// 存在spm(优先级高)
|
||||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/' + getIdBySpm(5))
|
||||
if (nav.value?.data) {
|
||||
form.value = nav.value.data
|
||||
}else{
|
||||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/getNavigationByPath',{query: {path: route.path}})
|
||||
if(nav.value?.data){
|
||||
form.value = nav.value?.data;
|
||||
}
|
||||
}
|
||||
// 页面布局
|
||||
if(form.value?.layout){
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
|
||||
// seo
|
||||
useHead({
|
||||
title: `${form.value.title} - ${website.value.websiteName}`,
|
||||
meta: [{ name: form.value.design?.keywords, content: form.value.design?.description }],
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
},
|
||||
script: [
|
||||
{
|
||||
children: `console.log(${JSON.stringify(form.value)})`,
|
||||
},
|
||||
],
|
||||
});
|
||||
// 面包屑
|
||||
breadcrumb.value = form.value
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
console.log(path,'=>Path')
|
||||
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
89
pages/article/[categoryId].vue
Normal file
89
pages/article/[categoryId].vue
Normal file
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<PageBanner :title="`${form?.title}`" :desc="`${form?.comments}`" />
|
||||
<CardList :list="list" :disabled="disabled" @done="onSearch" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import type {CompanyParam} from "~/api/system/company/model";
|
||||
import type {Article} from "~/api/cms/article/model";
|
||||
import CardList from './components/CardList.vue';
|
||||
import type {ArticleCategory} from "~/api/cms/category/model";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const list = ref<Article[]>([]);
|
||||
const page = ref<number>(1);
|
||||
const resultText = ref('');
|
||||
const layout = ref<any>();
|
||||
const disabled = ref<boolean>(false);
|
||||
const activeName = ref(undefined)
|
||||
|
||||
// 获取状态
|
||||
const form = ref<ArticleCategory>();
|
||||
const website = useWebsite();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
const handleClick = (e:any) => {
|
||||
console.log(e.index)
|
||||
}
|
||||
|
||||
const onSearch = () => {
|
||||
if(!disabled.value){
|
||||
page.value++;
|
||||
reload(route.path);
|
||||
}
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async (path: string) => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Article>>>('/cms/cms-article/page',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value,limit: 8,categoryId: route.params.categoryId, keywords: where.keywords
|
||||
}})
|
||||
if(response.value?.data){
|
||||
if (list.value.length < response.value?.data.count) {
|
||||
disabled.value = false;
|
||||
if (response.value?.data.list) {
|
||||
list.value = list.value.concat(response.value?.data.list);
|
||||
}
|
||||
}else {
|
||||
disabled.value = true;
|
||||
}
|
||||
if(response.value.data.count == 0){
|
||||
resultText.value = '暂无相关结果'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/getNavigationByPath',{query: {path: route.path}})
|
||||
if(nav.value?.data){
|
||||
form.value = nav.value?.data;
|
||||
}
|
||||
// 页面布局
|
||||
if(form.value?.layout){
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
|
||||
useHead({
|
||||
title: `文章列表 - ${website.value.websiteName}`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
reload(path);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
52
pages/article/components/CardList.vue
Normal file
52
pages/article/components/CardList.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<div class="md:w-screen-xl m-auto relative sm:flex" v-infinite-scroll="load">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :span="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer" @click="openSpmUrl(`/detail`,item,item.articleId,true)">
|
||||
<el-image :src="item.image" fit="fill" :lazy="true" class="w-full h-[150px] cursor-pointer" />
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="line-clamp-2 font-semibold text-gray-700 dark:text-white text-base gap-1.5 text-xl">
|
||||
<el-icon v-if="item.permission > 0"><Lock class="text-gray-400 pr-1"/></el-icon>
|
||||
{{ item.title }}
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500 line-clamp-3">{{ item.comments }}</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500">
|
||||
<el-avatar :src="item.avatar" :size="20"/>
|
||||
<span>{{ item.author }} · {{ dayjs(item.createTime).format('MM-DD hh:mm') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="disabled" class="px-1 text-center text-gray-500 min-h-xs">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
list?: any[];
|
||||
disabled?: boolean;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const load = () => {
|
||||
if(!props.disabled){
|
||||
emit('done')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
211
pages/ask/[userId].vue
Normal file
211
pages/ask/[userId].vue
Normal file
@@ -0,0 +1,211 @@
|
||||
<template>
|
||||
<UserCard :form="form" title="社区" desc="分享研发成果 交流技术经验" :avatar="user?.avatar"/>
|
||||
<CardList :list="list" :disabled="disabled" @done="onSearch" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import type {BreadcrumbItem} from "~/types/global";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import {getIdBySpm, openSpmUrl} from "~/utils/common";
|
||||
import type {Article} from "~/api/cms/article/model";
|
||||
import useFormData from "~/utils/use-form-data";
|
||||
import LikeArticle from "~/pages/detail/components/LikeArticle.vue";
|
||||
import CardList from "~/pages/ask/components/CardList.vue";
|
||||
import type {Company, CompanyParam} from "~/api/system/company/model";
|
||||
import type {User} from "~/api/system/user/model";
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
const website = useWebsite();
|
||||
const user = useUser();
|
||||
const breadcrumb = ref<BreadcrumbItem>();
|
||||
const previousArticle = ref<Article>();
|
||||
const nextArticle = ref<Article>();
|
||||
const disabled = ref<boolean>(false);
|
||||
const showContent = ref<boolean>();
|
||||
const showPassword = ref<boolean>();
|
||||
const list = ref<Article[]>([]);
|
||||
const page = ref<number>(1);
|
||||
|
||||
// 配置信息
|
||||
const {form, assignFields} = useFormData<Article>({
|
||||
// 文章id
|
||||
articleId: undefined,
|
||||
// 文章标题
|
||||
title: undefined,
|
||||
// 分类类型
|
||||
type: undefined,
|
||||
// 展现方式
|
||||
showType: undefined,
|
||||
// 文章类型
|
||||
categoryId: undefined,
|
||||
// 文章分类
|
||||
categoryName: undefined,
|
||||
parentId: undefined,
|
||||
parentName: undefined,
|
||||
parentPath: undefined,
|
||||
// 封面图
|
||||
image: undefined,
|
||||
// 附件
|
||||
files: undefined,
|
||||
// 缩列图
|
||||
thumbnail: undefined,
|
||||
// 视频地址
|
||||
video: undefined,
|
||||
// 上传的文件类型
|
||||
accept: undefined,
|
||||
// 来源
|
||||
source: undefined,
|
||||
// 文章内容
|
||||
content: undefined,
|
||||
// 虚拟阅读量
|
||||
virtualViews: undefined,
|
||||
// 实际阅读量
|
||||
actualViews: undefined,
|
||||
// 访问权限
|
||||
permission: undefined,
|
||||
// 访问密码
|
||||
password: undefined,
|
||||
password2: undefined,
|
||||
// 用户ID
|
||||
userId: undefined,
|
||||
// 用户昵称
|
||||
nickname: undefined,
|
||||
// 账号
|
||||
username: undefined,
|
||||
// 用户头像
|
||||
// userAvatar: undefined,
|
||||
author: undefined,
|
||||
// 所属门店ID
|
||||
shopId: undefined,
|
||||
//
|
||||
likes: undefined,
|
||||
// 排序
|
||||
sortNumber: undefined,
|
||||
// 备注
|
||||
comments: undefined,
|
||||
// 状态
|
||||
status: undefined,
|
||||
// 创建时间
|
||||
createTime: undefined,
|
||||
// 更新时间
|
||||
updateTime: undefined
|
||||
});
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
// 验证密码
|
||||
const checkPassword = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<unknown>>('/cms/cms-article/checkArticlePassword', {
|
||||
query: {
|
||||
password: form?.password,
|
||||
password2: form.password2
|
||||
}
|
||||
})
|
||||
if (response.value?.code === 0) {
|
||||
showPassword.value = false;
|
||||
showContent.value = true;
|
||||
} else {
|
||||
ElMessage.error(response.value?.message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const onSearch = () => {
|
||||
if(!disabled.value){
|
||||
page.value++;
|
||||
reload(route.path);
|
||||
}
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
// const reload = async (path: string) => {
|
||||
//
|
||||
// if (response.value?.data) {
|
||||
// if (list.value.length < response.value?.data.count) {
|
||||
// disabled.value = false;
|
||||
// if (response.value?.data.list) {
|
||||
// list.value = list.value.concat(response.value?.data.list);
|
||||
// }
|
||||
// } else {
|
||||
// disabled.value = true;
|
||||
// }
|
||||
// if (response.value.data.count == 0) {
|
||||
// resultText.value = '暂无相关结果'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Article>>>('/cms/cms-article/page', {
|
||||
params: {
|
||||
page: page.value,
|
||||
limit: 8,
|
||||
userId: route.params.userId,
|
||||
keywords: where.keywords
|
||||
}
|
||||
})
|
||||
if(response.value?.data){
|
||||
if (list.value.length < response.value?.data.count) {
|
||||
disabled.value = false;
|
||||
if (response.value?.data.list) {
|
||||
list.value = list.value.concat(response.value?.data.list);
|
||||
}
|
||||
}else {
|
||||
disabled.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
const { data: userInfo } = await useServerRequest<ApiResult<User>>(`/system/user/getByUserId/${getIdBySpm(5)}`)
|
||||
console.log(userInfo.value)
|
||||
// if (response.value?.data) {
|
||||
// assignFields(response.value.data)
|
||||
// if (form.permission === 1) {
|
||||
// console.log('登录可见')
|
||||
// return;
|
||||
// }
|
||||
// if (form.permission === 2) {
|
||||
// console.log('需要密码')
|
||||
// showPassword.value = true;
|
||||
// return;
|
||||
// }
|
||||
// form.parentPath = getSpmUrl(`/category`, form, form.articleId);
|
||||
// console.log(form.parentPath)
|
||||
// }
|
||||
|
||||
// seo
|
||||
useHead({
|
||||
title: `${form.title} - ${website.value.websiteName}`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
// 面包屑
|
||||
breadcrumb.value = form
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
console.log(path, '=>Path')
|
||||
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.content {
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
55
pages/ask/components/CardList.vue
Normal file
55
pages/ask/components/CardList.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative" v-infinite-scroll="load">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer"
|
||||
@click="openSpmUrl(`/detail`,item,item.articleId,true)">
|
||||
<el-image :src="item.image" fit="fill" :lazy="true" class="w-full md:h-[150px] h-[199px] cursor-pointer"/>
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<!-- <div class="text-gray-700 dark:text-white text-base font-semibold flex items-center gap-1.5">-->
|
||||
<!-- <div class="flex items-center text-xl cursor-pointer max-w-md line-clamp-2"><el-icon-->
|
||||
<!-- v-if="item.permission > 0"><Lock class="text-gray-400 pr-1"/></el-icon>{{ item.title }}</div>-->
|
||||
<!-- </div>-->
|
||||
<div class="line-clamp-2 font-semibold text-gray-700 dark:text-white text-base gap-1.5 text-xl">
|
||||
{{ item.title }}
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500">
|
||||
<el-avatar :src="item.logo" :size="20" @click.stop="openSpmUrl(`/ask/${item.userId}`,item,item.userId)"/>
|
||||
<span>{{ item.tenantName }} · {{ dayjs(item.createTime).format('MM-DD hh:mm') }}</span>
|
||||
<el-icon v-if="item.permission > 0" :size="24"><Lock class="text-gray-400 px-1"/></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="disabled" class="px-1 text-center text-gray-500 min-h-xs">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import dayjs from "dayjs";
|
||||
import {Lock} from '@element-plus/icons-vue'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
list?: any[];
|
||||
disabled?: boolean;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const load = () => {
|
||||
if (!props.disabled) {
|
||||
emit('done')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
103
pages/ask/index.vue
Normal file
103
pages/ask/index.vue
Normal file
@@ -0,0 +1,103 @@
|
||||
<template>
|
||||
<PageBanner :layout="layout" title="社区" desc="分享研发成果 交流技术经验" />
|
||||
<el-tabs v-model="activeName" class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative pb-2" @tab-click="handleClick">
|
||||
<el-tab-pane label="轻松一刻" name="happy"></el-tab-pane>
|
||||
<el-tab-pane label="企业官网" name="website"></el-tab-pane>
|
||||
<el-tab-pane label="企业商城" name="weShop"></el-tab-pane>
|
||||
<el-tab-pane label="内容管理" name="cms"></el-tab-pane>
|
||||
<el-tab-pane label="点餐外卖" name="food"></el-tab-pane>
|
||||
<el-tab-pane label="派单系统" name="task"></el-tab-pane>
|
||||
<el-tab-pane label="办公OA" name="oa"></el-tab-pane>
|
||||
<el-tab-pane label="问答" name="ask"></el-tab-pane>
|
||||
<el-tab-pane label="分享" name="share"></el-tab-pane>
|
||||
</el-tabs>
|
||||
<CardList :list="list" :disabled="disabled" @done="onSearch" />
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import type {CompanyParam} from "~/api/system/company/model";
|
||||
import type {Article} from "~/api/cms/article/model";
|
||||
import CardList from './components/CardList.vue';
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const list = ref<Article[]>([]);
|
||||
const page = ref<number>(1);
|
||||
const resultText = ref('');
|
||||
const layout = ref<any>();
|
||||
const disabled = ref<boolean>(false);
|
||||
const activeName = ref(undefined)
|
||||
|
||||
// 获取状态
|
||||
const form = ref<Navigation>();
|
||||
const website = useWebsite();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
const handleClick = (e:any) => {
|
||||
console.log(e.index)
|
||||
}
|
||||
|
||||
const onSearch = () => {
|
||||
if(!disabled.value){
|
||||
page.value++;
|
||||
reload(route.path);
|
||||
}
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async (path: string) => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Article>>>('/cms/cms-article/page',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value,
|
||||
limit: 8,
|
||||
tenantId: getIdBySpm(1),
|
||||
keywords: where.keywords
|
||||
}})
|
||||
if(response.value?.data){
|
||||
if (list.value.length < response.value?.data.count) {
|
||||
disabled.value = false;
|
||||
if (response.value?.data.list) {
|
||||
list.value = list.value.concat(response.value?.data.list);
|
||||
}
|
||||
}else {
|
||||
disabled.value = true;
|
||||
}
|
||||
if(response.value.data.count == 0){
|
||||
resultText.value = '暂无相关结果'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/getNavigationByPath',{query: {path: route.path}})
|
||||
if(nav.value?.data){
|
||||
form.value = nav.value?.data;
|
||||
}
|
||||
// 页面布局
|
||||
if(form.value?.layout){
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
|
||||
useHead({
|
||||
title: `社区 - ${website.value.websiteName}`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
reload(path);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
63
pages/case/components/CardList.vue
Normal file
63
pages/case/components/CardList.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative" v-infinite-scroll="load">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer" @click="openSpmUrl(`/item`, item,item.companyId,true)">
|
||||
<el-image
|
||||
:src="item.image"
|
||||
:fit="fit" :lazy="true" class="w-full md:h-[150px] h-[199px] cursor-pointer bg-gray-50"/>
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col gap-1.5">
|
||||
<div class="flex-1 text-xl cursor-pointer flex items-center">
|
||||
{{ item.shortName }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500 line-clamp-2">{{ item.comments }}</div>
|
||||
</div>
|
||||
<div class="button-group flex justify-between items-center mt-3">
|
||||
<el-space class="flex items-end">
|
||||
<el-avatar size="small" :src="item.companyLogo" />
|
||||
<span class="text-gray-400 line-clamp-1 pr-2">{{ item.companyName }}</span>
|
||||
</el-space>
|
||||
<el-button @click.stop="loginByToken(item)">
|
||||
控制台
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="disabled" class="px-1 text-center text-gray-500 min-h-xs">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {loginByToken, openSpmUrl} from "~/utils/common";
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
list?: Company[];
|
||||
disabled?: boolean;
|
||||
fit?: any;
|
||||
}>(),
|
||||
{
|
||||
fit: 'cover'
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const load = () => {
|
||||
if(!props.disabled){
|
||||
emit('done')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
115
pages/case/components/PageBanner.vue
Normal file
115
pages/case/components/PageBanner.vue
Normal file
@@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<div class="banner m-auto relative sm:flex">
|
||||
<svg viewBox="0 0 1440 181" fill="none" xmlns="http://www.w3.org/2000/svg"
|
||||
class="pointer-events-none absolute w-full top-[-2px] transition-all text-green-5 flex-shrink-0 opacity-100 duration-[400ms] opacity-80 -z-10">
|
||||
<mask id="path-1-inside-1_414_5526" fill="white">
|
||||
<path d="M0 0H1440V181H0V0Z"></path>
|
||||
</mask>
|
||||
<path d="M0 0H1440V181H0V0Z" fill="url(#paint0_linear_414_5526)" fill-opacity="0.22"></path>
|
||||
<path d="M0 2H1440V-2H0V2Z" fill="url(#paint1_linear_414_5526)" mask="url(#path-1-inside-1_414_5526)"></path>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_414_5526" x1="720" y1="0" x2="720" y2="181" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_414_5526" x1="0" y1="90.5" x2="1440" y2="90.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor" stop-opacity="0"></stop>
|
||||
<stop offset="0.395" stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<div class="md:w-screen-xl m-auto md:p-0 px-4">
|
||||
<div class="py-8 sm:py-16" _path="/templates" _dir="" _draft="false" _partial="false" _locale=""
|
||||
_id="content:4.templates.yml" _type="yaml" _source="content" _file="4.templates.yml" _stem="4.templates"
|
||||
_extension="yml">
|
||||
<div class="gap-8 sm:gap-y-16 grid lg:grid-cols-2 lg:items-center" v-if="layout">
|
||||
<div class="">
|
||||
<h1
|
||||
class="text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-4xl lg:text-5xl">
|
||||
<span v-if="layout.title">{{ layout.title }}</span>
|
||||
<span v-if="layout.name">{{ layout.name }}</span>
|
||||
</h1>
|
||||
|
||||
<div class="mt-4 text-lg text-gray-500 dark:text-gray-400">
|
||||
{{ layout.description }}
|
||||
</div>
|
||||
<div class="flex justify-between w-full items-center mt-4">
|
||||
<el-space>
|
||||
<div class="w-[500px]">
|
||||
<el-input
|
||||
v-model="where.keywords"
|
||||
placeholder="搜索"
|
||||
:prefix-icon="Search"
|
||||
@keydown.enter="handleClick"
|
||||
>
|
||||
<template #append>
|
||||
<el-button size="large" type="primary" @click="handleClick">搜索</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<el-button
|
||||
size="large"
|
||||
@click="openSpmUrl(`/passport/login`)"
|
||||
>
|
||||
创建
|
||||
</el-button>
|
||||
</el-space>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import {useConfigInfo} from "~/composables/configState";
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import type {CompanyParam} from "~/api/system/company/model";
|
||||
|
||||
const token = useToken();
|
||||
const sysDomain = useSysDomain();
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
layout?: any;
|
||||
title?: string;
|
||||
desc?: string;
|
||||
buyUrl?: string;
|
||||
}>(),
|
||||
{
|
||||
title: 'Templates',
|
||||
desc: 'Explore community templates to get up and running in a few seconds.',
|
||||
demoUrl: '/product/website',
|
||||
buyUrl: 'https://github.com/websoft9/ansible-templates'
|
||||
}
|
||||
);
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
const config = useConfigInfo();
|
||||
// 搜索结果
|
||||
const handleClick = async () => {
|
||||
if (where.keywords == '') {
|
||||
return false;
|
||||
}
|
||||
if(activeName.value == 'web'){
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Website>>>('/cms/cms-website/page',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value, keywords: where.keywords
|
||||
}})
|
||||
if(response.value?.data){
|
||||
if (response.value?.data.list) {
|
||||
websites.value = response.value?.data.list;
|
||||
}
|
||||
if(response.value.data.count == 0){
|
||||
resultText.value = '暂无相关结果'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
84
pages/case/index.vue
Normal file
84
pages/case/index.vue
Normal file
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<PageBanner :layout="layout" :title="`${form?.categoryName}`" :desc="`${form?.comments}`" />
|
||||
<CardList :param="{type: 0,official: true}" :list="list" :disabled="disabled" @done="onSearch" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import type {Company, CompanyParam} from "~/api/system/company/model";
|
||||
import CardList from './components/CardList.vue';
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const list = ref<Company[]>([]);
|
||||
const page = ref<number>(1);
|
||||
const resultText = ref('');
|
||||
const layout = ref<any>();
|
||||
const disabled = ref<boolean>(false);
|
||||
|
||||
// 获取状态
|
||||
const form = ref<Navigation>();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
const onSearch = () => {
|
||||
if(!disabled.value){
|
||||
page.value++;
|
||||
reload(route.path);
|
||||
}
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async (path: string) => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Company>>>('/system/company/pageAll',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value,
|
||||
limit: 8,
|
||||
categoryId: getIdBySpm(5),
|
||||
keywords: where.keywords
|
||||
}})
|
||||
if(response.value?.data){
|
||||
if (list.value.length < response.value?.data.count) {
|
||||
disabled.value = false;
|
||||
if (response.value?.data.list) {
|
||||
list.value = list.value.concat(response.value?.data.list);
|
||||
}
|
||||
}else {
|
||||
disabled.value = true;
|
||||
}
|
||||
if(response.value.data.count == 0){
|
||||
resultText.value = '暂无相关结果'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>(`/cms/cms-navigation/${getIdBySpm(5)}`)
|
||||
if(nav.value?.data){
|
||||
form.value = nav.value?.data;
|
||||
useHead({
|
||||
title: `${form.value.title} - WEBSOFT`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
}
|
||||
// 页面布局
|
||||
if(form.value?.layout){
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
|
||||
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
reload(path);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
47
pages/category/components/CardList.vue
Normal file
47
pages/category/components/CardList.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative" v-infinite-scroll="load">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer" @click="openSpmUrl(`/detail`,item,item.articleId,true)">
|
||||
<el-image :src="item.image" fit="fill" :lazy="true" class="w-full h-[150px] cursor-pointer" />
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col gap-1.5">
|
||||
<div class="flex-1 text-xl cursor-pointer">{{ item.title }}</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500">{{ item.comments }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="disabled" class="px-1 text-center text-gray-500 min-h-xs">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
list?: any[];
|
||||
disabled?: boolean;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const load = () => {
|
||||
if(!props.disabled){
|
||||
emit('done')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
91
pages/category/index.vue
Normal file
91
pages/category/index.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<PageBanner :title="`${form?.title}`" :desc="`${form?.comments}`" />
|
||||
<CardList :list="list" :disabled="disabled" @done="onSearch" />
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import type {CompanyParam} from "~/api/system/company/model";
|
||||
import type {Article} from "~/api/cms/article/model";
|
||||
import CardList from './components/CardList.vue';
|
||||
import type {ArticleCategory} from "~/api/cms/category/model";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const list = ref<Article[]>([]);
|
||||
const page = ref<number>(1);
|
||||
const resultText = ref('');
|
||||
const layout = ref<any>();
|
||||
const disabled = ref<boolean>(false);
|
||||
const activeName = ref(undefined)
|
||||
|
||||
// 获取状态
|
||||
const form = ref<ArticleCategory>();
|
||||
const website = useWebsite();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
const handleClick = (e:any) => {
|
||||
console.log(e.index)
|
||||
}
|
||||
|
||||
const onSearch = () => {
|
||||
if(!disabled.value){
|
||||
page.value++;
|
||||
reload(route.path);
|
||||
}
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async (path: string) => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Article>>>('/cms/cms-article/page',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value,limit: 8,categoryId: getIdBySpm(4), keywords: where.keywords
|
||||
}})
|
||||
if(response.value?.data){
|
||||
if (list.value.length < response.value?.data.count) {
|
||||
disabled.value = false;
|
||||
if (response.value?.data.list) {
|
||||
list.value = list.value.concat(response.value?.data.list);
|
||||
}
|
||||
}else {
|
||||
disabled.value = true;
|
||||
}
|
||||
if(response.value.data.count == 0){
|
||||
resultText.value = '暂无相关结果'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>(`/cms/cms-navigation/${getIdBySpm(4)}`)
|
||||
if(nav.value?.data){
|
||||
form.value = nav.value?.data;
|
||||
console.log(form);
|
||||
}
|
||||
// 页面布局
|
||||
// if(form.value?.layout){
|
||||
// layout.value = JSON.parse(form.value?.layout)
|
||||
// }
|
||||
|
||||
useHead({
|
||||
title: `文章列表 - ${website.value.websiteName}`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
reload(path);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
104
pages/components/ArticleList.vue
Normal file
104
pages/components/ArticleList.vue
Normal file
@@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<div class="text-center flex flex-col items-center py-10">
|
||||
<h2 class="text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-4xl lg:text-5xl">
|
||||
{{ title }}
|
||||
</h2>
|
||||
<div class="sub-title">
|
||||
<p class="text-gray-500 dark:text-gray-400 py-3">
|
||||
{{ comments }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative" v-infinite-scroll="load">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer"
|
||||
@click="openSpmUrl(`/detail`,item,item.articleId,true)">
|
||||
<el-image :src="item.image" fit="cover" :lazy="true" class="w-full md:h-[150px] h-[199px] cursor-pointer"/>
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex items-center gap-1.5">
|
||||
<span class="flex-1 text-xl cursor-pointer max-h-[57px] overflow-hidden">{{ item.title }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500">
|
||||
<el-avatar :src="item.avatar" :size="20"/>
|
||||
<span>{{ item.author }} · {{ dayjs(item.createTime).format('MM-DD hh:mm') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="disabled" class="p-4 text-center text-gray-500">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import dayjs from "dayjs";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import type {Article} from "~/api/cms/article/model";
|
||||
import type {CompanyParam} from "~/api/system/company/model";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
list?: any[];
|
||||
disabled?: boolean;
|
||||
title?: string;
|
||||
comments?: string;
|
||||
}>(),
|
||||
{
|
||||
title: '卡片标题',
|
||||
comments: '卡片描述'
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const list = ref<Article[]>([]);
|
||||
const page = ref<number>(1);
|
||||
const resultText = ref('');
|
||||
const disabled = ref(false);
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
const load = () => {
|
||||
if (!props.disabled) {
|
||||
page.value++;
|
||||
reload();
|
||||
}
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Article>>>('/cms/cms-article/page', {
|
||||
baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value, limit: 8, keywords: where.keywords
|
||||
}
|
||||
})
|
||||
if (response.value?.data) {
|
||||
if (list.value.length < response.value?.data.count) {
|
||||
disabled.value = false;
|
||||
if (response.value?.data.list) {
|
||||
list.value = list.value.concat(response.value?.data.list);
|
||||
}
|
||||
} else {
|
||||
disabled.value = true;
|
||||
}
|
||||
if (response.value.data.count == 0) {
|
||||
resultText.value = '暂无相关结果'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reload();
|
||||
</script>
|
||||
96
pages/components/Banner.vue
Normal file
96
pages/components/Banner.vue
Normal file
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<div class="banner m-auto relative sm:flex">
|
||||
<svg viewBox="0 0 1440 181" fill="none" xmlns="http://www.w3.org/2000/svg"
|
||||
class="pointer-events-none absolute w-full top-[-2px] transition-all text-green-5 flex-shrink-0 opacity-100 duration-[400ms] opacity-80 -z-10">
|
||||
<mask id="path-1-inside-1_414_5526" fill="white">
|
||||
<path d="M0 0H1440V181H0V0Z"></path>
|
||||
</mask>
|
||||
<path d="M0 0H1440V181H0V0Z" fill="url(#paint0_linear_414_5526)" fill-opacity="0.22"></path>
|
||||
<path d="M0 2H1440V-2H0V2Z" fill="url(#paint1_linear_414_5526)" mask="url(#path-1-inside-1_414_5526)"></path>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_414_5526" x1="720" y1="0" x2="720" y2="181" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_414_5526" x1="0" y1="90.5" x2="1440" y2="90.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor" stop-opacity="0"></stop>
|
||||
<stop offset="0.395" stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<div class="md:w-screen-xl m-auto">
|
||||
<div class="py-8 sm:py-16" _path="/templates" _dir="" _draft="false" _partial="false" _locale=""
|
||||
_id="content:4.templates.yml" _type="yaml" _source="content" _file="4.templates.yml" _stem="4.templates"
|
||||
_extension="yml">
|
||||
<div class="gap-8 sm:gap-y-16 grid lg:grid-cols-2 lg:items-center" v-if="layout">
|
||||
<div class="">
|
||||
<h1
|
||||
class="text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-4xl lg:text-5xl">
|
||||
<span>WEBSOFT</span>
|
||||
<span v-if="layout.name">{{ layout.name }}</span>
|
||||
</h1>
|
||||
|
||||
<div class="mt-4 text-lg text-gray-500 dark:text-gray-400">
|
||||
{{ layout.description }}
|
||||
</div>
|
||||
|
||||
<el-space class="mt-4">
|
||||
<el-button
|
||||
:icon="ElIconView"
|
||||
size="large"
|
||||
v-if="layout.demoUrl"
|
||||
@click="openSpmUrl(layout.demoUrl)"
|
||||
>
|
||||
演示地址
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="layout.buyUrl"
|
||||
:icon="ElIconBottom"
|
||||
size="large"
|
||||
@click="openSpmUrl(layout.buyUrl)"
|
||||
>
|
||||
下载模版
|
||||
</el-button>
|
||||
<el-button
|
||||
:icon="ElIconMemo"
|
||||
size="large"
|
||||
v-if="layout.docUrl"
|
||||
@click="openSpmUrl(layout.docUrl)"
|
||||
>
|
||||
帮助文档
|
||||
</el-button>
|
||||
</el-space>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {useConfigInfo} from "~/composables/configState";
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
|
||||
const token = useToken();
|
||||
const sysDomain = useSysDomain();
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
layout?: any;
|
||||
title?: string;
|
||||
desc?: string;
|
||||
buyUrl?: string;
|
||||
}>(),
|
||||
{
|
||||
title: 'Templates',
|
||||
desc: 'Explore community templates to get up and running in a few seconds.',
|
||||
demoUrl: '/product/website',
|
||||
buyUrl: 'https://github.com/websoft9/ansible-templates'
|
||||
}
|
||||
);
|
||||
|
||||
const config = useConfigInfo();
|
||||
|
||||
</script>
|
||||
87
pages/components/CardList.vue
Normal file
87
pages/components/CardList.vue
Normal file
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<div class="text-center flex flex-col items-center pb-10">
|
||||
<h2 class="text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-4xl lg:text-5xl">
|
||||
{{ title }}
|
||||
</h2>
|
||||
<div class="sub-title">
|
||||
<p class="text-gray-500 dark:text-gray-400 py-3">
|
||||
{{ comments }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer">
|
||||
<el-image
|
||||
:src="`https://oss.wsdns.cn/20240925/e5e47100f4b6471395b3b81f834d2902.jpg?x-oss-process=image/resize,m_fixed,w_750/quality,Q_90`"
|
||||
fit="contain" :lazy="true" class="w-full md:h-[150px] h-[199px] cursor-pointer"/>
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col gap-1.5">
|
||||
<div class="flex-1 text-xl cursor-pointer">{{ item.title }}</div>
|
||||
<div class="text-red-500">¥{{ item.price }}</div>
|
||||
</div>
|
||||
<div v-if="item.price && item.price > 0" class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500">{{ item.comments }}</div>
|
||||
</div>
|
||||
<div class="button-group flex justify-center mt-3">
|
||||
<el-button class="w-full" size="large" :icon="ElIconView" @click="openSpmUrl('/item', item,item.goodsId,true)">
|
||||
查看详情
|
||||
</el-button>
|
||||
<el-button type="primary" size="large" v-if="item.price && item.price > 0" class="w-full" :icon="ElIconShoppingCart">购买
|
||||
</el-button>
|
||||
<el-button v-else class="w-full" size="large" :icon="ElIconShoppingCart">下载</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="disabled" class="px-1 text-center text-gray-500 min-h-xs">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import dayjs from "dayjs";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import type {Product} from "~/api/oa/product/model";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
disabled?: boolean;
|
||||
title?: string;
|
||||
comments?: string;
|
||||
}>(),
|
||||
{
|
||||
title: '卡片标题',
|
||||
comments: '卡片描述'
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const list = ref<Product[]>([]);
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Product>>>('/cms/cms-product/page', {
|
||||
baseURL: runtimeConfig.public.apiServer, params: {
|
||||
limit: 8
|
||||
}
|
||||
})
|
||||
if (response.value?.data) {
|
||||
if (response.value?.data.list) {
|
||||
list.value = response.value?.data.list;
|
||||
}
|
||||
}
|
||||
}
|
||||
reload();
|
||||
</script>
|
||||
121
pages/components/Flash.vue
Normal file
121
pages/components/Flash.vue
Normal file
@@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<svg viewBox="0 0 1440 181" fill="none" xmlns="http://www.w3.org/2000/svg"
|
||||
class="pointer-events-none absolute w-full md:top-[58px] top-[48px] transition-all text-green-5 flex-shrink-0 opacity-100 duration-[400ms] opacity-80 -z-10">
|
||||
<mask id="path-1-inside-1_414_5526" fill="white">
|
||||
<path d="M0 0H1440V181H0V0Z"></path>
|
||||
</mask>
|
||||
<path d="M0 0H1440V181H0V0Z" fill="url(#paint0_linear_414_5526)" fill-opacity="0.22"></path>
|
||||
<path d="M0 2H1440V-2H0V2Z" fill="url(#paint1_linear_414_5526)" mask="url(#path-1-inside-1_414_5526)"></path>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_414_5526" x1="720" y1="0" x2="720" y2="181" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_414_5526" x1="0" y1="90.5" x2="1440" y2="90.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor" stop-opacity="0"></stop>
|
||||
<stop offset="0.395" stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
<div class="py-24 sm:py-32 md:py-20 relative dark:bg-gradient-to-b from-gray-950 to-gray-900 md:pb-36">
|
||||
<svg data-v-835f5c7a="" width="100%" height="869" viewBox="0 0 1440 869" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg" class="absolute top-0 inset-x-0 w-full hidden lg:block">
|
||||
<g clip-path="url(#clip0_184_92145)" data-v-835f5c7a="">
|
||||
<path
|
||||
d="M1446 275.643L1436.02 274.982C1432.72 274.763 1429.41 275.328 1426.37 276.631L1422.65 278.224C1418.96 279.802 1414.9 280.292 1410.94 279.634L1407.1 278.996C1405.71 278.765 1404.34 278.393 1403.02 277.886L1386.31 271.473C1381.73 269.712 1376.57 270.248 1372.45 272.914L1365.76 277.234C1363.58 278.647 1360.7 278.343 1358.86 276.502V276.502C1356.72 274.374 1353.28 274.343 1351.11 276.433L1344.92 282.403C1340.44 286.717 1333.69 287.644 1328.21 284.694L1326 283.5C1320.44 280.508 1313.71 280.689 1308.33 283.976L1299.03 289.654L1285.59 296.014C1281.96 297.731 1278.9 300.451 1276.77 303.853L1272.93 309.987C1271.03 313.024 1268.38 315.524 1265.24 317.249L1253.92 323.468C1251.36 324.876 1249.11 326.805 1247.34 329.131L1235.04 345.236C1234.37 346.125 1233.62 346.958 1232.81 347.728L1215.19 364.444C1214.42 365.176 1213.59 365.84 1212.7 366.429L1203.62 372.477C1201 374.22 1197.49 373.737 1195.44 371.351V371.351C1193.96 369.62 1191.65 368.841 1189.42 369.318L1184.56 370.36C1181.01 371.12 1177.3 370.529 1174.16 368.701L1173.04 368.049C1169.18 365.802 1164.62 365.075 1160.26 366.01L1159.99 366.067C1155.52 367.024 1150.86 366.28 1146.91 363.98V363.98C1143.13 361.779 1138.68 361 1134.38 361.782L1133.26 361.987C1131.13 362.373 1128.96 362.429 1126.82 362.154L1112.21 360.275C1104.18 359.242 1096.27 362.923 1091.88 369.734L1084.6 381.033C1082.45 384.375 1078.52 386.117 1074.6 385.466V385.466C1071.16 384.895 1067.67 386.16 1065.41 388.803L1065.01 389.266C1060.89 394.06 1053.42 393.901 1049.52 388.934L1049.08 388.388C1047.21 386.002 1044.23 384.755 1041.21 385.091V385.091C1038.59 385.382 1035.98 384.478 1034.09 382.628L1033.56 382.103C1031.28 379.87 1028.11 378.808 1024.95 379.222L1016.74 380.299C1010.61 381.102 1005.15 384.564 1001.81 389.761L1000.9 391.175C1000.6 391.634 1000.34 392.116 1000.12 392.616V392.616C998.034 397.308 992.905 399.853 987.908 398.673L922.828 383.317C914.914 381.45 906.633 384.319 901.57 390.683L881.281 416.19C879.616 418.283 878.366 420.675 877.599 423.237L875.563 430.035C873.756 436.068 869.336 440.972 863.523 443.394L833.628 455.85C832.326 456.392 831.082 457.064 829.916 457.856L805.463 474.443L778.863 492.653C775.823 494.734 773.384 497.578 771.791 500.899L765.453 514.114C764.421 516.265 763.03 518.224 761.34 519.908L758.903 522.336C755.771 525.457 751.734 527.509 747.367 528.201L743.046 528.885C741.042 529.203 739.144 529.997 737.512 531.202V531.202C731.74 535.463 723.583 534.077 719.544 528.147L717.634 525.344C713.701 519.569 706.461 517.035 699.785 519.097L675.952 526.456C674.393 526.938 672.782 527.236 671.153 527.345L642.47 529.257C640.814 529.368 639.178 529.674 637.595 530.169L612.206 538.115C610.18 538.749 608.069 539.072 605.946 539.073L591.266 539.081C586.947 539.084 582.733 537.755 579.198 535.276L559.251 521.29C555.716 518.81 551.502 517.481 547.183 517.484L539.808 517.488L530.734 517.493C527.487 517.495 524.285 518.249 521.379 519.698L509.732 525.502C507.382 526.673 504.835 527.393 502.22 527.625L491.78 528.551C484.992 529.153 478.596 533.199 472.305 535.82C467.807 537.694 461.469 539.153 452.93 539.158C442.362 539.164 430.624 545.001 421.043 551.514C414.832 555.737 394.283 564.38 386.773 564.384V564.384C385.87 564.385 383.099 564.544 382.239 564.815C348.971 575.322 338.889 556.976 329.255 551.758C326.102 550.05 322.456 551.498 319.469 553.484L297.943 567.793C291.932 571.788 284.289 572.417 277.706 569.457L266.573 564.451L242.539 554.686C236.104 552.071 228.793 552.807 223.009 556.652L184.235 582.425C182.617 583.501 180.859 584.346 179.009 584.938L130.261 600.533C128.197 601.194 126.042 601.531 123.874 601.532L100.333 601.545C91.5959 601.55 83.769 596.145 80.6786 587.973L76.9595 578.138C73.8692 569.967 66.5498 566.889 58.67 570.662C46.6763 576.405 29.4795 584.13 19.9733 584.135C4.84173 584.144 -41.9003 571.137 -63.3798 564.633"
|
||||
stroke="url(#paint0_linear_184_92145)" stroke-width="2" class="svg-elem-1" data-v-835f5c7a=""></path>
|
||||
<path
|
||||
d="M1464.92 266.565L1450.57 258.267C1447.82 256.676 1444.74 255.731 1441.57 255.502L1436.18 255.114C1433 254.885 1429.8 255.384 1426.84 256.574L1422.15 258.457C1418.84 259.79 1415.23 260.254 1411.69 259.805L1407.6 259.288C1405.82 259.063 1404.08 258.613 1402.42 257.948L1385.77 251.281C1381.25 249.468 1376.13 249.961 1372.03 252.602L1365.37 256.895C1363.2 258.296 1360.33 257.964 1358.54 256.102V256.102C1356.45 253.933 1352.99 253.888 1350.85 256L1344.61 262.135C1340.07 266.609 1333.08 267.464 1327.59 264.218L1325.87 263.202C1320.56 260.065 1313.95 260.115 1308.69 263.332L1299.08 269.214L1284.88 276.221C1281.48 277.899 1278.6 280.467 1276.54 283.651L1272.28 290.24C1270.48 293.024 1268.05 295.343 1265.18 297.006L1253.35 303.868C1251.14 305.147 1249.19 306.818 1247.58 308.797L1233.46 326.192L1215.42 344.393C1214.55 345.279 1213.58 346.074 1212.55 346.768L1203.86 352.58C1201.21 354.352 1197.67 353.932 1195.51 351.588V351.588C1194.04 349.997 1191.88 349.245 1189.74 349.584L1184.41 350.428C1180.68 351.018 1176.87 350.318 1173.6 348.444L1173.02 348.115C1169.38 346.028 1165.13 345.248 1160.98 345.905L1159.15 346.195C1154.81 346.882 1150.37 346.14 1146.5 344.083V344.083C1142.62 342.025 1138.18 341.283 1133.84 341.97L1133.07 342.092C1131.11 342.402 1129.12 342.434 1127.15 342.185L1112.1 340.284C1104.04 339.267 1096.13 342.983 1091.76 349.828L1084.59 361.072C1082.36 364.575 1078.29 366.458 1074.17 365.894L1073.85 365.85C1070.48 365.389 1067.11 366.665 1064.89 369.24V369.24C1060.85 373.921 1053.58 373.876 1049.6 369.147L1048.46 367.792C1046.58 365.566 1043.68 364.484 1040.81 364.939V364.939C1038.29 365.338 1035.72 364.557 1033.85 362.82L1033.03 362.054C1030.64 359.833 1027.36 358.834 1024.14 359.345L1015.19 360.762C1009.89 361.602 1005.11 364.442 1001.84 368.698L1000.47 370.478C999.972 371.121 999.555 371.821 999.226 372.562V372.562C997.235 377.05 992.33 379.484 987.552 378.355L922.041 362.871C914.12 360.999 905.832 363.872 900.769 370.244L880.463 395.798C878.8 397.892 877.552 400.284 876.786 402.845L874.743 409.675C872.938 415.71 868.519 420.616 862.705 423.04L832.785 435.515C831.483 436.058 830.238 436.731 829.071 437.523L804.603 454.135L777.983 472.375C774.943 474.457 772.505 477.303 770.913 480.626L764.567 493.874C763.536 496.025 762.147 497.985 760.457 499.67L758.015 502.105C754.882 505.23 750.842 507.285 746.471 507.978L742.149 508.662C740.144 508.98 738.244 509.776 736.61 510.983V510.983C730.836 515.249 722.673 513.861 718.634 507.926L716.717 505.109C712.78 499.325 705.531 496.787 698.846 498.852L675.006 506.218C673.445 506.7 671.833 506.999 670.202 507.108L641.499 509.019C639.842 509.129 638.204 509.436 636.619 509.932L611.215 517.886C609.187 518.521 607.074 518.845 604.948 518.846L590.257 518.851C585.937 518.853 581.72 517.522 578.184 515.039L558.214 501.021C554.678 498.539 550.462 497.208 546.141 497.209L538.759 497.212L529.68 497.216C526.43 497.217 523.224 497.973 520.316 499.423L508.663 505.235C506.312 506.408 503.762 507.129 501.145 507.361L490.698 508.287C483.905 508.889 477.506 512.939 471.212 515.564C466.71 517.441 460.368 518.902 451.82 518.905C441.243 518.91 429.495 524.758 419.907 531.282C413.69 535.513 393.144 544.162 385.625 544.165V544.165C384.72 544.165 381.942 544.324 381.08 544.597C347.792 555.114 337.699 536.736 328.058 531.508C324.902 529.796 321.253 531.246 318.264 533.235L296.726 547.564C290.711 551.566 283.061 552.194 276.473 549.228L265.331 544.212L241.278 534.426C234.839 531.806 227.521 532.543 221.733 536.394L182.935 562.205C181.317 563.281 179.557 564.128 177.706 564.721L128.926 580.335C126.859 580.996 124.703 581.333 122.532 581.334L98.9645 581.343C90.2275 581.347 82.4012 575.94 79.3124 567.767L75.5718 557.87C72.4831 549.697 65.1651 546.62 57.2861 550.396C45.2841 556.147 28.069 563.887 18.5533 563.891"
|
||||
stroke="url(#paint1_linear_184_92145)" stroke-opacity="0.5" stroke-width="2" class="svg-elem-2"
|
||||
data-v-835f5c7a=""></path>
|
||||
<path
|
||||
d="M1467.92 245.565L1453.57 237.268C1450.82 235.676 1447.74 234.73 1444.57 234.501L1439.19 234.113C1436 233.884 1432.81 234.383 1429.85 235.573L1425.16 237.455C1421.85 238.788 1418.25 239.251 1414.7 238.803L1410.61 238.285C1408.84 238.061 1407.1 237.611 1405.44 236.946L1388.79 230.277C1384.27 228.465 1379.15 228.957 1375.05 231.599L1368.39 235.891C1366.22 237.292 1363.36 236.96 1361.57 235.098V235.098C1359.48 232.929 1356.02 232.884 1353.87 234.996L1347.64 241.13C1343.1 245.604 1336.11 246.459 1330.62 243.213L1328.9 242.197C1323.6 239.059 1316.99 239.109 1311.73 242.326L1302.12 248.207L1287.93 255.212C1284.53 256.891 1281.65 259.459 1279.59 262.644L1275.33 269.231C1273.54 272.016 1271.1 274.335 1268.23 275.998L1256.41 282.858C1254.2 284.138 1252.25 285.809 1250.64 287.789L1236.52 305.182L1218.49 323.383C1217.61 324.268 1216.65 325.064 1215.61 325.757L1206.93 331.568C1204.29 333.341 1200.74 332.92 1198.58 330.577V330.577C1197.12 328.986 1194.95 328.234 1192.81 328.572L1187.48 329.416C1183.76 330.006 1179.95 329.306 1176.67 327.432L1176.1 327.103C1172.45 325.016 1168.21 324.236 1164.06 324.893L1162.23 325.182C1157.9 325.869 1153.46 325.127 1149.58 323.069V323.069C1145.7 321.011 1141.26 320.27 1136.93 320.956L1136.16 321.078C1134.2 321.388 1132.21 321.419 1130.24 321.171L1115.2 319.27C1107.14 318.253 1099.22 321.97 1094.86 328.816L1087.69 340.057C1085.46 343.559 1081.39 345.441 1077.28 344.878L1076.96 344.834C1073.59 344.373 1070.21 345.648 1067.99 348.223V348.223C1063.95 352.903 1056.69 352.859 1052.71 348.13L1051.57 346.774C1049.69 344.549 1046.79 343.466 1043.92 343.921V343.921C1041.4 344.32 1038.84 343.539 1036.97 341.802L1036.14 341.036C1033.76 338.815 1030.48 337.816 1027.26 338.326L1018.32 339.742C1013.01 340.583 1008.23 343.423 1004.96 347.681L1003.59 349.459C1003.09 350.102 1002.68 350.801 1002.35 351.543V351.543C1000.36 356.03 995.454 358.464 990.676 357.334L925.189 341.85C917.267 339.977 908.976 342.85 903.913 349.224L883.617 374.774C881.954 376.867 880.706 379.259 879.941 381.821L877.899 388.649C876.095 394.685 871.676 399.592 865.862 402.016L835.953 414.488C834.651 415.031 833.406 415.704 832.238 416.497L807.778 433.108L781.167 451.345C778.127 453.428 775.69 456.274 774.098 459.597L767.754 472.844C766.724 474.995 765.334 476.956 763.645 478.641L761.206 481.074C758.073 484.199 754.032 486.255 749.66 486.948L745.343 487.631C743.338 487.949 741.438 488.745 739.805 489.952V489.952C734.033 494.218 725.872 492.83 721.835 486.896L719.916 484.077C715.981 478.294 708.732 475.755 702.049 477.821L678.218 485.184C676.656 485.667 675.043 485.966 673.413 486.074L644.719 487.984C643.062 488.094 641.424 488.401 639.838 488.897L614.444 496.85C612.415 497.485 610.301 497.809 608.175 497.809L593.493 497.815C589.171 497.816 584.954 496.484 581.418 494.001L561.456 479.984C557.919 477.501 553.702 476.169 549.381 476.171L542.004 476.173L532.93 476.177C529.679 476.178 526.472 476.934 523.563 478.385L511.917 484.194C509.566 485.367 507.015 486.088 504.397 486.32L493.96 487.246C487.166 487.848 480.766 491.899 474.471 494.524C469.971 496.4 463.631 497.86 455.088 497.863C444.513 497.867 432.767 503.715 423.181 510.24C416.966 514.47 396.43 523.117 388.912 523.12V523.12C388.007 523.12 385.228 523.279 384.366 523.552C351.09 534.066 340.998 515.692 331.36 510.461C328.204 508.748 324.554 510.199 321.564 512.188L300.039 526.512C294.023 530.515 286.371 531.144 279.782 528.177L268.647 523.162L244.605 513.377C238.165 510.756 230.844 511.493 225.055 515.345L186.274 541.15C184.656 542.227 182.895 543.075 181.043 543.667L132.28 559.278C130.213 559.94 128.055 560.277 125.885 560.278L102.328 560.286C93.5913 560.289 85.7651 554.883 82.6767 546.709L78.9369 536.812C75.8485 528.639 68.5309 525.562 60.652 529.339C48.6535 535.09 31.4455 542.827 21.933 542.831C6.79419 542.836 -39.973 529.798 -61.4641 523.278"
|
||||
stroke="url(#paint2_linear_184_92145)" stroke-opacity="0.25" stroke-width="2" class="svg-elem-3"
|
||||
data-v-835f5c7a=""></path>
|
||||
<path
|
||||
d="M1464.92 224.565L1450.57 216.268C1447.82 214.676 1444.74 213.73 1441.57 213.501L1436.19 213.113C1433 212.884 1429.81 213.383 1426.85 214.573L1422.16 216.455C1418.85 217.788 1415.25 218.251 1411.7 217.803L1407.61 217.285C1405.84 217.061 1404.1 216.611 1402.44 215.946L1385.79 209.277C1381.27 207.465 1376.15 207.957 1372.05 210.599L1365.39 214.891C1363.22 216.292 1360.36 215.96 1358.57 214.098V214.098C1356.48 211.929 1353.02 211.884 1350.87 213.996L1344.64 220.13C1340.1 224.604 1333.11 225.459 1327.62 222.213L1325.9 221.197C1320.6 218.059 1313.99 218.109 1308.73 221.326L1299.12 227.207L1284.93 234.212C1281.53 235.891 1278.65 238.459 1276.59 241.644L1272.33 248.231C1270.54 251.016 1268.1 253.335 1265.23 254.998L1253.41 261.858C1251.2 263.138 1249.25 264.809 1247.64 266.789L1233.52 284.182L1215.49 302.383C1214.61 303.268 1213.65 304.064 1212.61 304.757L1203.93 310.568C1201.29 312.341 1197.74 311.92 1195.58 309.577V309.577C1194.12 307.986 1191.95 307.234 1189.81 307.572L1184.48 308.416C1180.76 309.006 1176.95 308.306 1173.67 306.432L1173.1 306.103C1169.45 304.016 1165.21 303.236 1161.06 303.893L1159.23 304.182C1154.9 304.869 1150.46 304.127 1146.58 302.069V302.069C1142.7 300.011 1138.26 299.27 1133.93 299.956L1133.16 300.078C1131.2 300.388 1129.21 300.419 1127.24 300.171L1112.2 298.27C1104.14 297.253 1096.22 300.97 1091.86 307.816L1084.69 319.057C1082.46 322.559 1078.39 324.441 1074.28 323.878L1073.96 323.834C1070.59 323.373 1067.21 324.648 1064.99 327.223V327.223C1060.95 331.903 1053.69 331.859 1049.71 327.13L1048.57 325.774C1046.69 323.549 1043.79 322.466 1040.92 322.921V322.921C1038.4 323.32 1035.84 322.539 1033.97 320.802L1033.14 320.036C1030.76 317.815 1027.48 316.816 1024.26 317.326L1015.32 318.742C1010.01 319.583 1005.23 322.423 1001.96 326.681L1000.59 328.459C1000.09 329.102 999.678 329.801 999.349 330.543V330.543C997.359 335.03 992.454 337.464 987.676 336.334L922.189 320.85C914.267 318.977 905.976 321.85 900.913 328.224L880.617 353.774C878.954 355.867 877.706 358.259 876.941 360.821L874.899 367.649C873.095 373.685 868.676 378.592 862.862 381.016L832.953 393.488C831.651 394.031 830.406 394.704 829.238 395.497L804.778 412.108L778.167 430.345C775.127 432.428 772.69 435.274 771.098 438.597L764.754 451.844C763.724 453.995 762.334 455.956 760.645 457.641L758.206 460.074C755.073 463.199 751.032 465.255 746.66 465.948L742.343 466.631C740.338 466.949 738.438 467.745 736.805 468.952V468.952C731.033 473.218 722.872 471.83 718.835 465.896L716.916 463.077C712.981 457.294 705.732 454.755 699.049 456.821L675.218 464.184C673.656 464.667 672.043 464.966 670.413 465.074L641.719 466.984C640.062 467.094 638.424 467.401 636.838 467.897L611.444 475.85C609.415 476.485 607.301 476.809 605.175 476.809L590.493 476.815C586.171 476.816 581.954 475.484 578.418 473.001L558.456 458.984C554.919 456.501 550.702 455.169 546.381 455.171L539.004 455.173L529.93 455.177C526.679 455.178 523.472 455.934 520.563 457.385L508.917 463.194C506.566 464.367 504.015 465.088 501.397 465.32L490.96 466.246C484.166 466.848 477.766 470.899 471.471 473.524C466.971 475.4 460.631 476.86 452.088 476.863C441.513 476.867 429.767 482.715 420.181 489.24C413.966 493.47 393.43 502.117 385.912 502.12V502.12C385.007 502.12 382.228 502.279 381.366 502.552C348.09 513.066 337.998 494.692 328.36 489.461C325.204 487.748 321.554 489.199 318.564 491.188L297.039 505.512C291.023 509.515 283.371 510.144 276.782 507.177L265.647 502.162L241.605 492.377C235.165 489.756 227.844 490.493 222.055 494.345L183.274 520.15C181.656 521.227 179.895 522.075 178.043 522.667L129.28 538.278C127.213 538.94 125.055 539.277 122.885 539.278L99.3284 539.286C90.5913 539.289 82.7651 533.883 79.6767 525.709L75.9369 515.812C72.8485 507.639 65.5309 504.562 57.652 508.339C45.6535 514.09 28.4455 521.827 18.933 521.831C3.79419 521.836 -42.973 508.798 -64.4641 502.278"
|
||||
stroke="url(#paint3_linear_184_92145)" stroke-opacity="0.1" stroke-width="2" class="svg-elem-4"
|
||||
data-v-835f5c7a=""></path>
|
||||
</g>
|
||||
<defs data-v-835f5c7a="">
|
||||
<linearGradient id="paint0_linear_184_92145" x1="1419.37" y1="282.77" x2="3.81323" y2="550.717"
|
||||
gradientUnits="userSpaceOnUse" data-v-835f5c7a="">
|
||||
<stop stop-color="#00DC82" stop-opacity="0" data-v-835f5c7a=""></stop>
|
||||
<stop offset="0.203125" stop-color="#00DC82" data-v-835f5c7a=""></stop>
|
||||
<stop offset="0.333238" stop-color="#00DC82" stop-opacity="0" data-v-835f5c7a=""></stop>
|
||||
<stop offset="0.66715" stop-color="#00DC82" stop-opacity="0" data-v-835f5c7a=""></stop>
|
||||
<stop offset="0.757084" stop-color="#00DC82" data-v-835f5c7a=""></stop>
|
||||
<stop offset="0.994792" stop-color="#00DC82" stop-opacity="0" data-v-835f5c7a=""></stop>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_184_92145" x1="1433.93" y1="259.854" x2="-4.14577" y2="537.487"
|
||||
gradientUnits="userSpaceOnUse" data-v-835f5c7a="">
|
||||
<stop stop-color="#00DC82" stop-opacity="0" data-v-835f5c7a=""></stop>
|
||||
<stop offset="0.203125" stop-color="#00DC82" data-v-835f5c7a=""></stop>
|
||||
<stop offset="0.333238" stop-color="#00DC82" stop-opacity="0" data-v-835f5c7a=""></stop>
|
||||
<stop offset="0.66715" stop-color="#00DC82" stop-opacity="0" data-v-835f5c7a=""></stop>
|
||||
<stop offset="0.757084" stop-color="#00DC82" data-v-835f5c7a=""></stop>
|
||||
<stop offset="0.994792" stop-color="#00DC82" stop-opacity="0" data-v-835f5c7a=""></stop>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_184_92145" x1="1438.07" y1="238.674" x2="6.64425" y2="510.046"
|
||||
gradientUnits="userSpaceOnUse" data-v-835f5c7a="">
|
||||
<stop stop-color="#00DC82" stop-opacity="0" data-v-835f5c7a=""></stop>
|
||||
<stop offset="0.203125" stop-color="#00DC82" data-v-835f5c7a=""></stop>
|
||||
<stop offset="0.333238" stop-color="#00DC82" stop-opacity="0" data-v-835f5c7a=""></stop>
|
||||
<stop offset="0.66715" stop-color="#00DC82" stop-opacity="0" data-v-835f5c7a=""></stop>
|
||||
<stop offset="0.757084" stop-color="#00DC82" data-v-835f5c7a=""></stop>
|
||||
<stop offset="0.994792" stop-color="#00DC82" stop-opacity="0" data-v-835f5c7a=""></stop>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_184_92145" x1="1435.07" y1="217.674" x2="3.64425" y2="489.046"
|
||||
gradientUnits="userSpaceOnUse" data-v-835f5c7a="">
|
||||
<stop stop-color="#00DC82" stop-opacity="0" data-v-835f5c7a=""></stop>
|
||||
<stop offset="0.203125" stop-color="#00DC82" data-v-835f5c7a=""></stop>
|
||||
<stop offset="0.333238" stop-color="#00DC82" stop-opacity="0" data-v-835f5c7a=""></stop>
|
||||
<stop offset="0.66715" stop-color="#00DC82" stop-opacity="0" data-v-835f5c7a=""></stop>
|
||||
<stop offset="0.757084" stop-color="#00DC82" data-v-835f5c7a=""></stop>
|
||||
<stop offset="0.994792" stop-color="#00DC82" stop-opacity="0" data-v-835f5c7a=""></stop>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_184_92145" data-v-835f5c7a="">
|
||||
<rect width="1491" height="732" fill="white" transform="translate(-73 144) rotate(-8)" class="svg-elem-5"
|
||||
data-v-835f5c7a=""></rect>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
<div class="mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl gap-16 sm:gap-y-24 flex flex-col ">
|
||||
<div class="text-center relative z-[1]">
|
||||
<div class="mb-8 cursor-pointer">
|
||||
<el-tag type="warning" round @click="openSpmUrl(`/detail`, {},730,true)">v3.0 版本发布
|
||||
</el-tag>
|
||||
</div>
|
||||
<h1 class="text-5xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-7xl">
|
||||
<span>构建现代Web应用</span><br/>
|
||||
<span class="text-primary block lg:inline-block text-green-500">Vue 框架</span>
|
||||
</h1>
|
||||
<div class="mt-6 text-lg tracking-tight text-gray-600 dark:text-gray-300"> WEBSOFT是一个基于Vue和Nuxt构建的web框架,使web开发更直观而强大<br>
|
||||
自信地创建高性能和生产级的全栈web应用程序和网站
|
||||
</div>
|
||||
<div class="mt-10 flex flex-wrap gap-x-6 gap-y-3 justify-center">
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex items-center">
|
||||
<el-button size="large" type="primary" :icon="ElIconArrowRight" @click="openSpmUrl(`/passport/login`)">立即开始</el-button>
|
||||
<!-- <el-button size="large" type="primary" v-else :icon="ElIconArrowRight" @click="loginAdminByToken">进入控制台</el-button>-->
|
||||
<el-button size="large" type="success" :icon="ElIconDownload" @click="openSpmUrl(`http://git.gxwebsoft.com/free`, {},0,true)">源码下载</el-button>
|
||||
<el-button size="large" type="danger" @click="openSpmUrl(`https://website.websoft.top`, {},123)">商业版演示</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {useToken} from "~/composables/configState";
|
||||
import {loginAdminByToken, openSpmUrl} from "~/utils/common";
|
||||
const token = useToken();
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
83
pages/components/PlugList.vue
Normal file
83
pages/components/PlugList.vue
Normal file
@@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<div class="text-center flex flex-col items-center py-10">
|
||||
<h2 class="text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-4xl lg:text-5xl">
|
||||
插件市场
|
||||
</h2>
|
||||
<div class="sub-title">
|
||||
<p class="text-gray-500 dark:text-gray-400 py-3">
|
||||
安装插件,几秒钟内即可启动并运行
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer">
|
||||
<el-image
|
||||
:src="item.image"
|
||||
fit="contain" :lazy="true" class="w-full md:h-[150px] h-[199px] cursor-pointer bg-gray-50"/>
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col gap-1.5">
|
||||
<div class="flex-1 text-xl cursor-pointer">{{ item.title }}</div>
|
||||
<div class="text-red-500">¥{{ item.price }}</div>
|
||||
</div>
|
||||
<div v-if="item.price && item.price > 0" class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500">{{ item.comments }}</div>
|
||||
</div>
|
||||
<div class="button-group flex justify-center mt-3">
|
||||
<el-button class="w-full" size="large" :icon="ElIconView" @click="openSpmUrl(`/item`, item,item.productId,true)">查看详情</el-button>
|
||||
<el-button type="primary" size="large" v-if="item.price && item.price > 0" class="w-full" :icon="ElIconShoppingCart">购买
|
||||
</el-button>
|
||||
<el-button v-else class="w-full" size="large" :icon="ElIconShoppingCart">下载</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="disabled" class="px-1 text-center text-gray-500 min-h-xs">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import dayjs from "dayjs";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import type {Plug} from "~/api/system/plug/model";
|
||||
import type {Product} from "~/api/oa/product/model";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
disabled?: boolean;
|
||||
type?: number;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const list = ref<Product[]>([]);
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Product>>>('/cms/cms-product/page', {
|
||||
baseURL: runtimeConfig.public.apiServer, params: {
|
||||
limit: 8,
|
||||
type: props.type,
|
||||
}
|
||||
})
|
||||
if (response.value?.data) {
|
||||
if (response.value?.data.list) {
|
||||
list.value = response.value?.data.list;
|
||||
}
|
||||
}
|
||||
}
|
||||
reload();
|
||||
</script>
|
||||
92
pages/components/ProductList.vue
Normal file
92
pages/components/ProductList.vue
Normal file
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<div class="text-center flex flex-col items-center py-12">
|
||||
<h2 class="text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-4xl lg:text-5xl">
|
||||
{{ title }}
|
||||
</h2>
|
||||
<div class="sub-title">
|
||||
<p class="text-gray-500 dark:text-gray-400 py-3">
|
||||
{{ comments }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer" @click="openSpmUrl(`/item`, item,item.productId,true)">
|
||||
<el-image
|
||||
:src="item.image"
|
||||
fit="cover" :lazy="true" class="w-full md:h-[150px] h-[199px] cursor-pointer bg-gray-50"/>
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col gap-1.5">
|
||||
<div class="flex-1 text-xl cursor-pointer flex items-center">
|
||||
{{ item.title }}
|
||||
<el-tag v-if="item.tag == '1'" size="small" type="success" class="text-white ml-2" effect="dark">免费</el-tag>
|
||||
<el-tag v-if="item.tag == '2'" size="small" type="success" class="text-white ml-2" effect="dark">开源</el-tag>
|
||||
<el-tag v-if="item.tag == '3'" size="small" type="danger" class="text-white ml-2" effect="dark">付费</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500 line-clamp-3">{{ item.comments }}</div>
|
||||
</div>
|
||||
<div class="button-group flex justify-between items-center mt-3">
|
||||
<div v-if="item.price && item.price > 0" class="text-red-500 text-xl">¥{{ item.price }} </div>
|
||||
<el-button v-if="item.price && item.price > 0" @click.stop="openSpmUrl(`/product/create`,item,item.productId,true)">立即开通
|
||||
</el-button>
|
||||
<el-button v-else>下载</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="disabled" class="px-1 text-center text-gray-500 min-h-xs">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import dayjs from "dayjs";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import type {Product} from "~/api/oa/product/model";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
disabled?: boolean;
|
||||
title?: string;
|
||||
type?: number;
|
||||
comments?: string;
|
||||
}>(),
|
||||
{
|
||||
title: '卡片标题',
|
||||
comments: '卡片描述'
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const list = ref<Product[]>([]);
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Product>>>('/cms/cms-product/page', {
|
||||
baseURL: runtimeConfig.public.apiServer, params: {
|
||||
type: props.type,
|
||||
limit: 8,
|
||||
status: 0
|
||||
}
|
||||
})
|
||||
if (response.value?.data) {
|
||||
if (response.value?.data.list) {
|
||||
list.value = response.value?.data.list;
|
||||
}
|
||||
}
|
||||
}
|
||||
reload();
|
||||
</script>
|
||||
64
pages/detail/components/LikeArticle.vue
Normal file
64
pages/detail/components/LikeArticle.vue
Normal file
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<el-affix class="lg:p-0 p-3" v-if="form" :offset="offset" @change="onAffix">
|
||||
<el-button type="text" color="gray">
|
||||
<div class="text-xl text-gray-600 ">你可能还喜欢</div>
|
||||
<el-icon class="el-icon--right text-gray-600" color="#999999">
|
||||
<ArrowRightBold/>
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer mt-3"
|
||||
@click="openSpmUrl(`/detail`,item,item.articleId,true)">
|
||||
<el-image :src="item.image" fit="fill" :lazy="true" class="w-full md:h-[150px] h-[199px] cursor-pointer"/>
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex items-center gap-1.5">
|
||||
<span class="flex-1 text-xl cursor-pointer max-h-[57px] overflow-hidden">{{ item.title }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500">
|
||||
<el-avatar :src="item.avatar" :size="20"/>
|
||||
<span>{{ item.author }} · {{ dayjs(item.createTime).format('MM-DD hh:mm') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
</el-affix>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ArrowRightBold } from '@element-plus/icons-vue'
|
||||
import {getIdBySpm, openSpmUrl} from "~/utils/common";
|
||||
import dayjs from "dayjs";
|
||||
import type {Article} from "~/api/cms/article/model";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
title?: string;
|
||||
form?: Article;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const list = ref<Article[]>([]);
|
||||
const isAffix = ref<any>();
|
||||
const offset = ref(70)
|
||||
|
||||
const onAffix = (e: any) => {
|
||||
isAffix.value = e;
|
||||
}
|
||||
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Article>>>('/cms/cms-article/page', {
|
||||
params: {
|
||||
categoryId: getIdBySpm(4),
|
||||
limit: 3
|
||||
}
|
||||
})
|
||||
if (response.value?.data) {
|
||||
list.value = response.value.data.list;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
94
pages/detail/components/PageBanner.vue
Normal file
94
pages/detail/components/PageBanner.vue
Normal file
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<div class="banner m-auto relative sm:flex">
|
||||
<svg viewBox="0 0 1440 181" fill="none" xmlns="http://www.w3.org/2000/svg"
|
||||
class="pointer-events-none absolute w-full top-[-2px] transition-all text-green-5 flex-shrink-0 opacity-100 duration-[400ms] opacity-80 -z-10">
|
||||
<mask id="path-1-inside-1_414_5526" fill="white">
|
||||
<path d="M0 0H1440V181H0V0Z"></path>
|
||||
</mask>
|
||||
<path d="M0 0H1440V181H0V0Z" fill="url(#paint0_linear_414_5526)" fill-opacity="0.22"></path>
|
||||
<path d="M0 2H1440V-2H0V2Z" fill="url(#paint1_linear_414_5526)" mask="url(#path-1-inside-1_414_5526)"></path>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_414_5526" x1="720" y1="0" x2="720" y2="181" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_414_5526" x1="0" y1="90.5" x2="1440" y2="90.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor" stop-opacity="0"></stop>
|
||||
<stop offset="0.395" stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<div class="md:w-screen-xl m-auto px-3">
|
||||
<Breadcrumb :data="form" />
|
||||
<div class="md:py-8 sm:py-16 md:px-0 px-4 py-8" _path="/templates" _dir="" _draft="false" _partial="false" _locale=""
|
||||
_id="content:4.templates.yml" _type="yaml" _source="content" _file="4.templates.yml" _stem="4.templates"
|
||||
_extension="yml">
|
||||
<div class="gap-8 sm:gap-y-16 lg:items-center" v-if="form">
|
||||
<div class="w-full">
|
||||
<h1
|
||||
class="text-2xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-3xl lg:text-4xl">
|
||||
<span v-if="form.title">{{ form.title }}</span>
|
||||
</h1>
|
||||
<el-space class="mt-4 text-gray-500 dark:text-gray-400">
|
||||
<el-avatar size="small" :src="form.logo" />
|
||||
<span class="cursor-pointer text-gray-500" @click="openSpmUrl(`/ask/${form.userId}`)">{{ form.tenantName }}</span> · {{ dayjs(form.createTime).format('YYYY-MM-DD hh:mm') }}
|
||||
</el-space>
|
||||
|
||||
<!-- <el-space class="mt-4">-->
|
||||
<!-- <el-button-->
|
||||
<!-- :icon="ElIconView"-->
|
||||
<!-- size="large"-->
|
||||
<!-- v-if="form.demoUrl"-->
|
||||
<!-- @click="openSpmUrl(form.demoUrl)"-->
|
||||
<!-- >-->
|
||||
<!-- 演示地址-->
|
||||
<!-- </el-button>-->
|
||||
<!-- <el-button-->
|
||||
<!-- v-if="form.buyUrl"-->
|
||||
<!-- :icon="ElIconBottom"-->
|
||||
<!-- size="large"-->
|
||||
<!-- @click="openSpmUrl(form.buyUrl)"-->
|
||||
<!-- >-->
|
||||
<!-- 下载模版-->
|
||||
<!-- </el-button>-->
|
||||
<!-- <el-button-->
|
||||
<!-- :icon="ElIconMemo"-->
|
||||
<!-- size="large"-->
|
||||
<!-- v-if="form.docUrl"-->
|
||||
<!-- @click="openSpmUrl(form.docUrl)"-->
|
||||
<!-- >-->
|
||||
<!-- 帮助文档-->
|
||||
<!-- </el-button>-->
|
||||
<!-- </el-space>-->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import Breadcrumb from "~/components/Breadcrumb.vue";
|
||||
import type {Article} from "~/api/cms/article/model";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
title?: string;
|
||||
desc?: string;
|
||||
buyUrl?: string;
|
||||
form?: Article;
|
||||
}>(),
|
||||
{
|
||||
title: 'Templates',
|
||||
desc: 'Explore community templates to get up and running in a few seconds.',
|
||||
demoUrl: '/product/website',
|
||||
buyUrl: 'https://github.com/websoft9/ansible-templates'
|
||||
}
|
||||
);
|
||||
|
||||
</script>
|
||||
232
pages/detail/index.vue
Normal file
232
pages/detail/index.vue
Normal file
@@ -0,0 +1,232 @@
|
||||
<!-- 文章详情 -->
|
||||
<template>
|
||||
<PageBanner :form="form"/>
|
||||
<div class="page-main md:w-screen-xl m-auto md:px-3" ref="container">
|
||||
<el-row :gutter="24">
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="18" class="min-w-xs">
|
||||
<template v-if="form.permission === 0 || showContent">
|
||||
<div class="p-6 leading-7 bg-white md:rounded-lg text-lg line-height-loose text-gray-600 content">
|
||||
<p v-html="form?.content"></p>
|
||||
<template v-for="(item,index) in form.fileList" :key="index" class="text item">
|
||||
<el-image
|
||||
:src="item"
|
||||
:zoom-rate="1.2"
|
||||
:max-scale="7"
|
||||
:min-scale="0.2"
|
||||
:preview-src-list="form.fileList"
|
||||
:initial-index="4"
|
||||
fit="contain"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="form.permission === 1">
|
||||
<el-result
|
||||
v-if="!showContent && form.permission === 1"
|
||||
icon="warning"
|
||||
title="无访问权限"
|
||||
>
|
||||
</el-result>
|
||||
</template>
|
||||
<template v-if="form.permission === 2">
|
||||
<el-result
|
||||
v-if="!showContent && form.permission === 2"
|
||||
icon="warning"
|
||||
title="请输入访问密码"
|
||||
>
|
||||
<template #extra>
|
||||
<el-space>
|
||||
<el-input type="password" v-model="form.password2" show-password placeholder="请输入查看密码">请输入查看密码</el-input>
|
||||
<el-button type="primary" @click="checkPassword">确定</el-button>
|
||||
</el-space>
|
||||
</template>
|
||||
</el-result>
|
||||
</template>
|
||||
<div class="page md:gap-xl gap-xs md:mt-4 m-auto md:p-0 p-4 flex justify-between">
|
||||
<div v-if="previousArticle" class="bg-white text-gray-600 hover:shadow hover:text-gray-800 hover:font-bold rounded-lg p-4 cursor-pointer w-[50%]"
|
||||
@click="openSpmUrl(`/detail`,previousArticle,previousArticle?.articleId)">
|
||||
<span>上一篇:{{ previousArticle?.title }}</span>
|
||||
</div>
|
||||
<div v-if="nextArticle" class="bg-white text-gray-600 hover:shadow hover:text-gray-800 hover:font-bold rounded-lg p-4 cursor-pointer w-[50%]"
|
||||
@click="openSpmUrl(`/detail`,nextArticle,nextArticle?.articleId)">
|
||||
<span>下一篇:{{ nextArticle?.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<!-- <el-col :xs="24" :sm="6" :md="4" :lg="3" :xl="6">-->
|
||||
<el-col :xs="24" :sm="6" :md="6" :lg="6">
|
||||
<!-- 你可能感兴趣 -->
|
||||
<LikeArticle v-if="form" :form="form" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import type {BreadcrumbItem} from "~/types/global";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import {getIdBySpm, openSpmUrl} from "~/utils/common";
|
||||
import type {Article} from "~/api/cms/article/model";
|
||||
import useFormData from "~/utils/use-form-data";
|
||||
import PageBanner from './components/PageBanner.vue';
|
||||
import LikeArticle from "./components/LikeArticle.vue";
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
const website = useWebsite();
|
||||
const breadcrumb = ref<BreadcrumbItem>();
|
||||
const previousArticle = ref<Article>();
|
||||
const nextArticle = ref<Article>();
|
||||
const showContent = ref<boolean>();
|
||||
const showPassword = ref<boolean>();
|
||||
|
||||
// 配置信息
|
||||
const {form, assignFields} = useFormData<Article>({
|
||||
// 文章id
|
||||
articleId: undefined,
|
||||
// 文章标题
|
||||
title: undefined,
|
||||
// 分类类型
|
||||
type: undefined,
|
||||
// 展现方式
|
||||
showType: undefined,
|
||||
// 文章类型
|
||||
categoryId: undefined,
|
||||
// 文章分类
|
||||
categoryName: undefined,
|
||||
parentId: undefined,
|
||||
parentName: undefined,
|
||||
parentPath: undefined,
|
||||
// 封面图
|
||||
image: undefined,
|
||||
// 附件
|
||||
files: undefined,
|
||||
// 附件
|
||||
fileList: [],
|
||||
// 缩列图
|
||||
thumbnail: undefined,
|
||||
// 视频地址
|
||||
video: undefined,
|
||||
// 上传的文件类型
|
||||
accept: undefined,
|
||||
// 来源
|
||||
source: undefined,
|
||||
// 文章内容
|
||||
content: undefined,
|
||||
// 虚拟阅读量
|
||||
virtualViews: undefined,
|
||||
// 实际阅读量
|
||||
actualViews: undefined,
|
||||
// 访问权限
|
||||
permission: undefined,
|
||||
// 访问密码
|
||||
password: undefined,
|
||||
password2: undefined,
|
||||
// 用户ID
|
||||
userId: undefined,
|
||||
// 用户昵称
|
||||
nickname: undefined,
|
||||
// 账号
|
||||
username: undefined,
|
||||
// 用户头像
|
||||
// userAvatar: undefined,
|
||||
author: undefined,
|
||||
// 所属门店ID
|
||||
shopId: undefined,
|
||||
//
|
||||
likes: undefined,
|
||||
// 排序
|
||||
sortNumber: undefined,
|
||||
// 备注
|
||||
comments: undefined,
|
||||
// 状态
|
||||
status: undefined,
|
||||
// 创建时间
|
||||
createTime: undefined,
|
||||
// 更新时间
|
||||
updateTime: undefined,
|
||||
// 租户名称
|
||||
tenantName: undefined,
|
||||
// 租户logo
|
||||
logo: undefined
|
||||
});
|
||||
|
||||
// 验证密码
|
||||
const checkPassword = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<unknown>>('/cms/cms-article/checkArticlePassword', {
|
||||
query: {
|
||||
password: form?.password,
|
||||
password2: form.password2
|
||||
}
|
||||
})
|
||||
if(response.value?.code === 0){
|
||||
showPassword.value = false;
|
||||
showContent.value = true;
|
||||
}else {
|
||||
ElMessage.error(response.value?.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
|
||||
// 存在spm(优先级高)
|
||||
const {data: nav} = await useServerRequest<ApiResult<Navigation>>('/cms/cms-article/' + getIdBySpm(5))
|
||||
if (nav.value?.data) {
|
||||
assignFields(nav.value.data)
|
||||
if(form.permission === 1){
|
||||
console.log('登录可见')
|
||||
return;
|
||||
}
|
||||
if(form.permission === 2){
|
||||
console.log('需要密码')
|
||||
showPassword.value = true;
|
||||
return;
|
||||
}
|
||||
form.parentPath = getSpmUrl(`/category`,form,form.articleId);
|
||||
console.log(form.parentPath)
|
||||
}
|
||||
|
||||
// 上一篇
|
||||
const {data: previous} = await useServerRequest<ApiResult<Navigation>>('/cms/cms-article/getPrevious/' + getIdBySpm(5))
|
||||
if (previous.value?.data) {
|
||||
previousArticle.value = previous.value.data
|
||||
}
|
||||
|
||||
// 下一篇
|
||||
const {data: next} = await useServerRequest<ApiResult<Navigation>>('/cms/cms-article/getNext/' + getIdBySpm(5))
|
||||
if (next.value?.data) {
|
||||
nextArticle.value = next.value.data
|
||||
}
|
||||
|
||||
// seo
|
||||
useHead({
|
||||
title: `${form.title} - ${website.value.websiteName}`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
// 面包屑
|
||||
breadcrumb.value = form
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
console.log(path, '=>Path')
|
||||
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.content {
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
47
pages/developer/components/CardList.vue
Normal file
47
pages/developer/components/CardList.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative" v-infinite-scroll="load">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer" @click="openSpmUrl(`/detail`,item,item.articleId)">
|
||||
<el-image :src="item.image" fit="fill" :lazy="true" class="w-full h-[150px] cursor-pointer" />
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col gap-1.5">
|
||||
<div class="flex-1 text-xl cursor-pointer">{{ item.title }}</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500">{{ item.comments }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="disabled" class="px-1 text-center text-gray-500 min-h-xs">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
list?: any[];
|
||||
disabled?: boolean;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const load = () => {
|
||||
if(!props.disabled){
|
||||
emit('done')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
154
pages/developer/index.vue
Normal file
154
pages/developer/index.vue
Normal file
@@ -0,0 +1,154 @@
|
||||
<template>
|
||||
<PageBanner :layout="layout" title="入驻成为开发者" @done="onDone" />
|
||||
<div v-if="showContent" class="md:w-screen-xl md:px-0 px-4 m-auto relative sm:flex bg-white rounded-lg py-4 ">
|
||||
1231232
|
||||
<el-tabs class="px-4 bg-white" v-if="form">
|
||||
<el-tab-pane label="个人开发者认证">
|
||||
<el-form :model="form" label-width="auto" size="large" label-position="top" class="sm:w-screen-md w-full sm:py-2">
|
||||
<el-form-item label="真实姓名">
|
||||
<el-input v-model="form.realName" placeholder="请输入真实姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="证件号码">
|
||||
<el-input v-model="form.idCard" placeholder="请输入证件号码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="身份证(正面)">
|
||||
<Upload />
|
||||
</el-form-item>
|
||||
<el-form-item label="身份证(反面)">
|
||||
<Upload />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" size="large" @click="onSubmit">提交</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-result
|
||||
icon="success"
|
||||
title="个人认证成功"
|
||||
sub-title="认证完成时间 2024-09-30"
|
||||
>
|
||||
<template #extra>
|
||||
<el-button type="text">修改认证信息</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="企业开发者认证">
|
||||
<el-form :model="form" label-width="auto" size="large" label-position="top" class="sm:w-screen-md w-full sm:py-2">
|
||||
<el-form-item label="企业名称">
|
||||
<el-input v-model="form.companyName" placeholder="请输入企业名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="社会信用代码">
|
||||
<el-input v-model="form.idCard" placeholder="请输入社会信用代码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="营业执照">
|
||||
<Upload />
|
||||
</el-form-item>
|
||||
<el-form-item label="所属行业">
|
||||
<el-select v-model="form.city" placeholder="请选择所属行业">
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="网站信息">
|
||||
<el-input v-model="form.idCard" placeholder="请输入网站信息" />
|
||||
</el-form-item>
|
||||
<el-form-item label="您的身份">
|
||||
<el-radio-group v-model="form.sex">
|
||||
<el-radio value="1">法定代表人</el-radio>
|
||||
<el-radio value="2">被授权人</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户协议">
|
||||
<el-checkbox v-model="form.status">请务必提供真实信息,我司有权自行或委托第三方审查您提供的身份信息是否属真实,有效。若提供虚假信息,由此的全部后果由您承担。</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" size="large" @click="onSubmit">提交</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-result
|
||||
icon="success"
|
||||
title="企业认证成功"
|
||||
sub-title="认证完成时间 2024-09-30"
|
||||
>
|
||||
<template #extra>
|
||||
<el-button type="text">修改认证信息</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import type {CompanyParam} from "~/api/system/company/model";
|
||||
import type {Article} from "~/api/cms/article/model";
|
||||
import type {ShopMerchant} from "~/api/shop/shopMerchant/model";
|
||||
|
||||
const route = useRoute();
|
||||
// 页面信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const list = ref<Article[]>([]);
|
||||
const page = ref<number>(1);
|
||||
const resultText = ref('');
|
||||
const layout = ref<any>();
|
||||
const disabled = ref<boolean>(false);
|
||||
|
||||
// 获取状态
|
||||
const form = ref<Navigation>();
|
||||
const showContent = ref<boolean>(false);
|
||||
const website = useWebsite();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
const onDone = (index: boolean) => {
|
||||
showContent.value = index;
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async (path: string) => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Article>>>('/cms/cms-article/page',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value,
|
||||
limit: 8,
|
||||
userId: 0,
|
||||
keywords: where.keywords
|
||||
}})
|
||||
if(response.value?.data){
|
||||
if (list.value.length < response.value?.data.count) {
|
||||
disabled.value = false;
|
||||
if (response.value?.data.list) {
|
||||
list.value = list.value.concat(response.value?.data.list);
|
||||
}
|
||||
}else {
|
||||
disabled.value = true;
|
||||
}
|
||||
if(response.value.data.count == 0){
|
||||
resultText.value = '暂无相关结果'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const {data: response} = await useServerRequest<ApiResult<ShopMerchant>>('/shop/shop-merchant-apply/getByUserId')
|
||||
if (response.value?.data) {
|
||||
if (response.value.data.status == 2) {
|
||||
loginDeveloperCenterByToken();
|
||||
}
|
||||
}
|
||||
|
||||
useHead({
|
||||
title: `开发者中心 - ${website.value?.websiteName}`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
reload(path);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
149
pages/developer/join-in.vue
Normal file
149
pages/developer/join-in.vue
Normal file
@@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<PageBanner :layout="layout" />
|
||||
<div class="login-layout mt-[100px] m-auto sm:w-screen-xl w-full">
|
||||
<div class="mt-[100px] m-auto flex sm:flex-row flex-col sm:p-0 p-3">
|
||||
<div class="flash ml-8 bg-white rounded-lg px-7 py-4 w-full">
|
||||
<el-tabs class="flash bg-white ml-0">
|
||||
<el-tab-pane label="个人开发者">
|
||||
<el-form :model="form" label-width="auto" size="large" label-position="top" class="sm:w-screen-md w-full sm:py-2">
|
||||
<el-form-item label="真实姓名">
|
||||
<el-input v-model="form.realName" placeholder="请输入真实姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="证件号码">
|
||||
<el-input v-model="form.idCard" placeholder="请输入证件号码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="身份证(正面)">
|
||||
<Upload />
|
||||
</el-form-item>
|
||||
<el-form-item label="身份证(反面)">
|
||||
<Upload />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" size="large" @click="onSubmit">提交</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-result
|
||||
icon="success"
|
||||
title="个人认证成功"
|
||||
sub-title="认证完成时间 2024-09-30"
|
||||
>
|
||||
<template #extra>
|
||||
<el-button type="text">修改认证信息</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="企业开发者">
|
||||
<el-form :model="form" label-width="auto" size="large" label-position="top" class="sm:w-screen-md w-full sm:py-2">
|
||||
<el-form-item label="企业名称">
|
||||
<el-input v-model="form.companyName" placeholder="请输入企业名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="社会信用代码">
|
||||
<el-input v-model="form.idCard" placeholder="请输入社会信用代码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="营业执照">
|
||||
<Upload />
|
||||
</el-form-item>
|
||||
<el-form-item label="所属行业">
|
||||
<el-select v-model="form.city" placeholder="请选择所属行业">
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="网站信息">
|
||||
<el-input v-model="form.idCard" placeholder="请输入网站信息" />
|
||||
</el-form-item>
|
||||
<el-form-item label="您的身份">
|
||||
<el-radio-group v-model="form.sex">
|
||||
<el-radio value="1">法定代表人</el-radio>
|
||||
<el-radio value="2">被授权人</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户协议">
|
||||
<el-checkbox v-model="form.status">请务必提供真实信息,我司有权自行或委托第三方审查您提供的身份信息是否属真实,有效。若提供虚假信息,由此的全部后果由您承担。</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" size="large" @click="onSubmit">提交</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-result
|
||||
icon="success"
|
||||
title="企业认证成功"
|
||||
sub-title="认证完成时间 2024-09-30"
|
||||
>
|
||||
<template #extra>
|
||||
<el-button type="text">修改认证信息</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {useConfigInfo, useToken, useWebsite} from "~/composables/configState";
|
||||
import useFormData from '@/utils/use-form-data';
|
||||
import type { User } from '@/api/system/user/model';
|
||||
import { ref } from 'vue'
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult} from "~/api";
|
||||
|
||||
// 配置信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const route = useRoute();
|
||||
const activeIndex = ref('');
|
||||
const website = useWebsite()
|
||||
const config = useConfigInfo();
|
||||
const token = useToken();
|
||||
const userInfo = ref<User>();
|
||||
|
||||
// 配置信息
|
||||
const { form, assignFields } = useFormData<User>({
|
||||
userId: undefined,
|
||||
nickname: '',
|
||||
username: '',
|
||||
phone: '',
|
||||
mobile: '',
|
||||
sex: '',
|
||||
sexName: '',
|
||||
email: '',
|
||||
password: '',
|
||||
code: '',
|
||||
smsCode: '',
|
||||
comments: '',
|
||||
remember: true
|
||||
});
|
||||
|
||||
useHead({
|
||||
title: `用户中心 - ${config.value?.siteName}`,
|
||||
meta: [{ name: website.value.keywords, content: website.value.comments }]
|
||||
});
|
||||
|
||||
const onSubmit = async () => {
|
||||
const {data: modify } = await useServerRequest<ApiResult<User>>('/auth/user',{
|
||||
baseURL: runtimeConfig.public.apiServer,
|
||||
method: 'put',
|
||||
body: form
|
||||
})
|
||||
if(modify.value?.code == 0){
|
||||
ElMessage.success('修改成功')
|
||||
}
|
||||
}
|
||||
|
||||
const reload = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<User>>('/auth/user',{baseURL: runtimeConfig.public.apiServer})
|
||||
if(response.value?.data){
|
||||
userInfo.value = response.value?.data;
|
||||
assignFields(response.value?.data);
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
activeIndex.value = path;
|
||||
console.log(path,'=>Path')
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
47
pages/docs/components/CardList.vue
Normal file
47
pages/docs/components/CardList.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative" v-infinite-scroll="load">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer" @click="openSpmUrl(`/detail`,item,item.articleId)">
|
||||
<el-image :src="item.image" fit="fill" :lazy="true" class="w-full h-[150px] cursor-pointer" />
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col gap-1.5">
|
||||
<div class="flex-1 text-xl cursor-pointer">{{ item.title }}</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500">{{ item.comments }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="disabled" class="px-1 text-center text-gray-500 min-h-xs">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
list?: any[];
|
||||
disabled?: boolean;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const load = () => {
|
||||
if(!props.disabled){
|
||||
emit('done')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
103
pages/docs/index.vue
Normal file
103
pages/docs/index.vue
Normal file
@@ -0,0 +1,103 @@
|
||||
<template>
|
||||
<PageBanner :layout="layout" title="文档" />
|
||||
<el-tabs v-model="activeName" class="md:w-screen-xl m-auto relative sm:flex pb-2" @tab-click="handleClick">
|
||||
<el-tab-pane label="企业官网" name="website"></el-tab-pane>
|
||||
<el-tab-pane label="企业商城" name="weShop"></el-tab-pane>
|
||||
<el-tab-pane label="内容管理" name="cms"></el-tab-pane>
|
||||
<el-tab-pane label="点餐外卖" name="food"></el-tab-pane>
|
||||
<el-tab-pane label="派单系统" name="task"></el-tab-pane>
|
||||
<el-tab-pane label="办公OA" name="oa"></el-tab-pane>
|
||||
<el-tab-pane label="常见问题" name="ask"></el-tab-pane>
|
||||
</el-tabs>
|
||||
<CardList :list="list" :disabled="disabled" @done="onSearch" />
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import type {CompanyParam} from "~/api/system/company/model";
|
||||
import type {Article} from "~/api/cms/article/model";
|
||||
import CardList from './components/CardList.vue';
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const list = ref<Article[]>([]);
|
||||
const page = ref<number>(1);
|
||||
const resultText = ref('');
|
||||
const layout = ref<any>();
|
||||
const disabled = ref<boolean>(false);
|
||||
const activeName = ref(undefined)
|
||||
|
||||
// 获取状态
|
||||
const form = ref<Navigation>();
|
||||
const website = useWebsite();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
const handleClick = (e:any) => {
|
||||
console.log(e.index)
|
||||
}
|
||||
|
||||
const onSearch = () => {
|
||||
if(!disabled.value){
|
||||
page.value++;
|
||||
reload(route.path);
|
||||
}
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async (path: string) => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Article>>>('/cms/cms-article/page',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value,
|
||||
limit: 8,
|
||||
userId: 0,
|
||||
keywords: where.keywords
|
||||
}})
|
||||
if(response.value?.data){
|
||||
if (list.value.length < response.value?.data.count) {
|
||||
disabled.value = false;
|
||||
if (response.value?.data.list) {
|
||||
list.value = list.value.concat(response.value?.data.list);
|
||||
}
|
||||
}else {
|
||||
disabled.value = true;
|
||||
}
|
||||
if(response.value.data.count == 0){
|
||||
resultText.value = '暂无相关结果'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/getNavigationByPath',{query: {path: route.path}})
|
||||
console.log(nav.value?.data)
|
||||
if(nav.value?.data){
|
||||
form.value = nav.value?.data;
|
||||
console.log(form.value,'form...')
|
||||
}
|
||||
// 页面布局
|
||||
if(form.value?.layout){
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
|
||||
useHead({
|
||||
title: `文档 - ${website.value.websiteName}`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
reload(path);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
52
pages/down/components/CardList.vue
Normal file
52
pages/down/components/CardList.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative" v-infinite-scroll="load">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer">
|
||||
<el-image :src="`${item.image}`" fit="contain" :lazy="true" class="w-full md:h-[150px] h-[199px] cursor-pointer" />
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col gap-1.5">
|
||||
<div class="flex-1 text-xl cursor-pointer">{{ item.title }}</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500">{{ item.comments }}</div>
|
||||
</div>
|
||||
<div class="button-group flex justify-center mt-3">
|
||||
<el-button class="w-full" size="large" :icon="ElIconView" @click="openSpmUrl(`/item`, item, item.productId,true)">在线体验</el-button>
|
||||
<el-button type="primary" size="large" class="w-full" :icon="ElIconShoppingCart">下载源码</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="disabled" class="px-1 text-center text-gray-500 min-h-xs">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import dayjs from "dayjs";
|
||||
import type {CmsProduct} from "~/api/cms/cmsProduct/model";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
list?: CmsProduct[];
|
||||
disabled?: boolean;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const load = () => {
|
||||
if(!props.disabled){
|
||||
emit('done')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
96
pages/down/index.vue
Normal file
96
pages/down/index.vue
Normal file
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<PageBanner :layout="layout" :title="`演示中心`"
|
||||
:desc="`拥抱开源、坚守品质;致力于打造安全稳定高可用的WEB应用!`"/>
|
||||
<!-- <div class="text-3xl py-5">{{ count }}</div>-->
|
||||
<!-- <el-button @click="increment">点击</el-button>-->
|
||||
<CardList :list="list" :disabled="disabled" @done="onSearch"/>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import type {CompanyParam} from "~/api/system/company/model";
|
||||
import CardList from './components/CardList.vue';
|
||||
import type {CmsProduct} from "~/api/cms/cmsProduct/model";
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const list = ref<CmsProduct[]>([]);
|
||||
const page = ref<number>(1);
|
||||
const resultText = ref('');
|
||||
const layout = ref<any>();
|
||||
const disabled = ref<boolean>(false);
|
||||
|
||||
// 获取状态
|
||||
const form = ref<Navigation>();
|
||||
const website = useWebsite();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
const onSearch = () => {
|
||||
if (!disabled.value) {
|
||||
page.value++;
|
||||
reload(route.path);
|
||||
}
|
||||
}
|
||||
const count = useToken()
|
||||
|
||||
function increment() {
|
||||
count.value = uuidv4()
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async (path: string) => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<CmsProduct>>>('/cms/cms-product/page', {
|
||||
baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value,
|
||||
limit: 8,
|
||||
price: 0,
|
||||
keywords: where.keywords
|
||||
}
|
||||
})
|
||||
if (response.value?.data) {
|
||||
if (list.value.length < response.value?.data.count) {
|
||||
disabled.value = false;
|
||||
if (response.value?.data.list) {
|
||||
list.value = list.value.concat(response.value?.data.list);
|
||||
}
|
||||
} else {
|
||||
disabled.value = true;
|
||||
}
|
||||
if (response.value.data.count == 0) {
|
||||
resultText.value = '暂无相关结果'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const {data: nav} = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/getNavigationByPath', {query: {path: route.path}})
|
||||
if (nav.value?.data) {
|
||||
form.value = nav.value?.data;
|
||||
}
|
||||
// 页面布局
|
||||
if (form.value?.layout) {
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
|
||||
useHead({
|
||||
title: `产品 - ${website.value.websiteName}`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
reload(path);
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
68
pages/index.vue
Normal file
68
pages/index.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
|
||||
<Flash/>
|
||||
|
||||
|
||||
<CompanyList :param="{official: true,recommend: true,limit: 4}" :fit="`cover`" />
|
||||
|
||||
<!-- <ProductList :param="{type:0, official: true,limit: 4}" :fit="`cover`" title="产品服务" comments="拥抱开源、坚守品质;致力于打造安全稳定高可用的WEB应用!"/>-->
|
||||
|
||||
<!-- <ArticleList title="开发者社区" comments="分享研发成果 交流技术经验"/>-->
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {useConfigInfo, useForm, useToken, useWebsite} from "~/composables/configState";
|
||||
import type {BreadcrumbItem} from "~/types/global";
|
||||
import Flash from './components/Flash.vue';
|
||||
import ArticleList from './components/ArticleList.vue';
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
const layout = ref<any>();
|
||||
const token = useToken();
|
||||
const form = useForm();
|
||||
const breadcrumb = ref<BreadcrumbItem>();
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
|
||||
// 页面布局
|
||||
if (form.value?.layout) {
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
|
||||
// 未登录状态(是否强制登录)
|
||||
if (!token.value || token.value == '') {
|
||||
// if (config.value.MustLogin) {
|
||||
// navigateTo('/passport/login');
|
||||
// return false;
|
||||
// }
|
||||
}
|
||||
|
||||
// seo
|
||||
useHead({
|
||||
title: `构建现代WEB应用 · WEBSOFT`,
|
||||
meta: [{name: form.value.design?.keywords, content: form.value.design?.description}],
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
},
|
||||
script: [
|
||||
{
|
||||
children: `console.log(${JSON.stringify(form.value)})`,
|
||||
},
|
||||
],
|
||||
});
|
||||
// 面包屑
|
||||
breadcrumb.value = form.value
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
console.log(path, '=>Path')
|
||||
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
201
pages/item/components/Comments.vue
Normal file
201
pages/item/components/Comments.vue
Normal file
@@ -0,0 +1,201 @@
|
||||
<template>
|
||||
<form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-position="top"
|
||||
class="w-full sm:py-2"
|
||||
size="large"
|
||||
status-icon
|
||||
>
|
||||
<el-card shadow="hover" v-if="comments" class="hover:border-green-50 hover:border-2 mb-5">
|
||||
<template #header>
|
||||
<div class="card-header font-bold text-xl flex justify-between">
|
||||
<span>评分和评价</span>
|
||||
<div class="comments">
|
||||
<el-button @click="onComplaint">投诉</el-button>
|
||||
<el-button type="primary" @click="onComments">发表评论</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #default>
|
||||
<template v-if="comments.length > 0">
|
||||
<div class="w-full">
|
||||
<div v-for="(item,index) in comments" :key="index"
|
||||
class="flex flex-col border-b-2 border-gray-200 pb-2 mb-3"
|
||||
style="border-bottom:1px solid #f3f3f3">
|
||||
<el-space class="user-info flex items-start" style="align-items:normal">
|
||||
<div class="avatar">
|
||||
<el-avatar :src="item.logo"/>
|
||||
</div>
|
||||
<div class="nickname flex flex-col">
|
||||
<el-space class="text-sm text-gray-900">
|
||||
<span class="font-bold">{{ item.tenantName }}</span>
|
||||
<el-rate v-model="item.rate" disabled size="small"/>
|
||||
</el-space>
|
||||
<span class="text-xs text-gray-400">{{ item.createTime }}</span>
|
||||
<div class="comments py-2" v-html="item.comments"></div>
|
||||
<template v-if="item.children" v-for="(sub,index2) in item.children" :key="index2">
|
||||
<el-space class="text-sm text-gray-900">
|
||||
<el-avatar :src="sub.logo" size="small"/>
|
||||
<span class="font-bold">{{ sub.tenantName }}</span>
|
||||
<span class="text-xs text-gray-400">{{ sub.createTime }}</span>
|
||||
</el-space>
|
||||
<div class="comments py-2" v-html="sub.comments"></div>
|
||||
</template>
|
||||
</div>
|
||||
</el-space>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pagination flex justify-center">
|
||||
<el-pagination background layout="prev, pager, next" size="small" :total="count" @change="onPageChange"/>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
暂无用户评论
|
||||
</template>
|
||||
</template>
|
||||
</el-card>
|
||||
<!-- 发表评论 -->
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
title="发表评论"
|
||||
align-center
|
||||
width="500"
|
||||
:before-close="() => visible = false"
|
||||
>
|
||||
<el-form-item prop="rate">
|
||||
<el-rate v-model="form.rate" size="large" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="comments">
|
||||
<el-input v-model="form.comments" :rows="5" type="textarea"
|
||||
placeholder="最多300字,支持 markdown 语法。请认真填写评论内容,以便于帮助作者更好完善插件,如需反馈问题,请到下方插件提问进行提交。"/>
|
||||
</el-form-item>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitForm(formRef)">
|
||||
提交
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<!-- 发起投诉 -->
|
||||
<el-dialog
|
||||
v-model="visible2"
|
||||
title="为什么举报此内容?"
|
||||
align-center
|
||||
width="500"
|
||||
:before-close="() => visible2 = false"
|
||||
>
|
||||
<el-checkbox-group v-model="checkList">
|
||||
<el-checkbox label="与我无关" value="与我无关"/>
|
||||
<el-checkbox label="文章过时" value="文章过时"/>
|
||||
<el-checkbox label="标题有误" value="标题有误"/>
|
||||
<el-checkbox label="图像质量差或视觉缺陷" value="图像质量差或视觉缺陷"/>
|
||||
<el-checkbox label="垃圾邮件" value="垃圾邮件"/>
|
||||
<el-checkbox label="成人或违法违规内容" value="成人或违法违规内容"/>
|
||||
<el-checkbox label="侵犯知识产权" value="侵犯知识产权"/>
|
||||
</el-checkbox-group>
|
||||
<div class="py-3">
|
||||
<el-input v-model="form.comments" :rows="5" type="textarea"
|
||||
placeholder="在此处输入反馈。请记住不要包含个人信息,如电话号码。"/>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="visible2 = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitForm(formRef)">
|
||||
提交
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {FullScreen} from '@element-plus/icons-vue'
|
||||
import type {ApiResult} from "~/api";
|
||||
import type {FormInstance, FormRules} from "element-plus";
|
||||
import {useClientRequest} from "~/composables/useClientRequest";
|
||||
import {reactive, ref} from "vue";
|
||||
import useFormData from "~/utils/use-form-data";
|
||||
import type {CompanyComment} from "~/api/system/companyComment/model";
|
||||
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
title?: string;
|
||||
companyId?: number;
|
||||
comments?: CompanyComment[];
|
||||
count?: number;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const visible = ref<boolean>(false);
|
||||
const visible2 = ref<boolean>(false);
|
||||
const checkList = ref<string[]>([]);
|
||||
const loading = ref<boolean>(true)
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done', page: number): void
|
||||
}>()
|
||||
|
||||
// 配置信息
|
||||
const {form, resetFields} = useFormData<CompanyComment>({
|
||||
id: undefined,
|
||||
parentId: undefined,
|
||||
userId: undefined,
|
||||
companyId: undefined,
|
||||
rate: undefined,
|
||||
sortNumber: undefined,
|
||||
comments: undefined,
|
||||
status: undefined,
|
||||
});
|
||||
|
||||
const rules = reactive<FormRules<any>>({
|
||||
rate: [
|
||||
{required: true, message: '请输入评分', trigger: 'blur'},
|
||||
],
|
||||
comments: [
|
||||
{required: true, message: '请输入手机号码', trigger: 'blur'},
|
||||
{pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur'},
|
||||
],
|
||||
})
|
||||
|
||||
const onComments = () => {
|
||||
visible.value = true;
|
||||
}
|
||||
|
||||
const onComplaint = () => {
|
||||
visible2.value = true;
|
||||
}
|
||||
|
||||
const onPageChange = (page: number) => {
|
||||
emit('done', page)
|
||||
}
|
||||
|
||||
const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
if (form.rate === 0) {
|
||||
ElMessage.error('还没有评分哦!')
|
||||
return false;
|
||||
}
|
||||
form.companyId = Number(getIdBySpm(5));
|
||||
useClientRequest<ApiResult<any>>(`/system/company-comment`, {
|
||||
method: 'POST',
|
||||
body: form
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
ElMessage.success(res.message)
|
||||
visible.value = false
|
||||
resetFields();
|
||||
emit('done',0)
|
||||
} else {
|
||||
return ElMessage.error(res.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
168
pages/item/components/PageBanner.vue
Normal file
168
pages/item/components/PageBanner.vue
Normal file
@@ -0,0 +1,168 @@
|
||||
<template>
|
||||
<div class="banner m-auto relative sm:flex">
|
||||
<svg viewBox="0 0 1440 181" fill="none" xmlns="http://www.w3.org/2000/svg"
|
||||
class="pointer-events-none absolute w-full top-[-2px] transition-all text-green-5 flex-shrink-0 opacity-100 duration-[400ms] opacity-80 -z-10">
|
||||
<mask id="path-1-inside-1_414_5526" fill="white">
|
||||
<path d="M0 0H1440V181H0V0Z"></path>
|
||||
</mask>
|
||||
<path d="M0 0H1440V181H0V0Z" fill="url(#paint0_linear_414_5526)" fill-opacity="0.22"></path>
|
||||
<path d="M0 2H1440V-2H0V2Z" fill="url(#paint1_linear_414_5526)" mask="url(#path-1-inside-1_414_5526)"></path>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_414_5526" x1="720" y1="0" x2="720" y2="181" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_414_5526" x1="0" y1="90.5" x2="1440" y2="90.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor" stop-opacity="0"></stop>
|
||||
<stop offset="0.395" stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<div class="md:w-screen-xl m-auto">
|
||||
<Breadcrumb :data="form"/>
|
||||
<div class="py-8 sm:py-16" _path="/templates" _dir="" _draft="false" _partial="false" _locale=""
|
||||
_id="content:4.templates.yml" _type="yaml" _source="content" _file="4.templates.yml" _stem="4.templates"
|
||||
_extension="yml">
|
||||
<div class="gap-8 sm:gap-y-16 lg:items-center" v-if="form">
|
||||
<div class="w-full sm:px-0 px-4">
|
||||
<div class="flex flex-1">
|
||||
<template v-if="form.companyLogo">
|
||||
<el-avatar :src="form.companyLogo" shape="square" :size="180"
|
||||
class="hidden-sm-and-down rounded-avatar shadow-sm hover:shadow mr-4"/>
|
||||
<el-avatar :src="form.companyLogo" shape="square" :size="80"
|
||||
class="hidden-sm-and-up rounded-avatar-xs shadow-sm hover:shadow mr-4"/>
|
||||
</template>
|
||||
<div class="title flex flex-col">
|
||||
<h1
|
||||
class="text-2xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-3xl lg:text-4xl">
|
||||
<span v-if="form.tenantName">{{ form.tenantName }}</span>
|
||||
</h1>
|
||||
<div class="my-1 text-sm text-gray-500 w-auto sm:max-w-3xl max-w-xs flex-1 dark:text-gray-400">
|
||||
{{ form?.comments || desc }}
|
||||
</div>
|
||||
<!-- <a class="company-name text-sm my-1">-->
|
||||
<!-- {{ form.companyName || 'WebSoft Inc.' }}-->
|
||||
<!-- </a>-->
|
||||
<el-rate v-model="form.rate" disabled />
|
||||
<div class="btn" v-if="form.companyId">
|
||||
<el-space class="mt-4">
|
||||
<template v-if="form.isBuy">
|
||||
<el-button v-if="form.installed" type="primary" @click.stop="loginDeveloperCenterByToken(form)">控制台</el-button>
|
||||
<el-button v-else type="primary" @click.stop="loginDeveloperCenterByToken(form)">控制台</el-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-button v-if="form.chargingMethod == 0" type="primary" @click.stop="loginDeveloperCenterByToken(form)">控制台</el-button>
|
||||
<el-button v-else type="warning" @click.stop="openSpmUrl(`/product/create`,form,form.companyId,true)">立即开通
|
||||
</el-button>
|
||||
</template>
|
||||
<!-- <el-button @click.stop="openSpmUrl(`https://${form.domain}`,form,form.companyId,true)">产品控制台</el-button>-->
|
||||
<el-button @click="openSpmUrl(`/ask`,form,form.companyId,true)">帮助文档</el-button>
|
||||
<!-- <template v-for="(item,index) in form.links" :key="index">-->
|
||||
<!-- <div v-if="item.qrcode">-->
|
||||
<!-- <el-popover-->
|
||||
<!-- placement="top-start"-->
|
||||
<!-- :width="200"-->
|
||||
<!-- trigger="hover"-->
|
||||
<!-- >-->
|
||||
<!-- <template #default>-->
|
||||
<!-- <div class=" p-2 flex justify-center">-->
|
||||
<!-- <el-image :src="item.qrcode" :size="160"/>-->
|
||||
<!-- </div>-->
|
||||
<!-- </template>-->
|
||||
<!-- <template #reference>-->
|
||||
<!-- <el-button :icon="ElIconFullScreen">{{ item.type }}</el-button>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-popover>-->
|
||||
<!-- </div>-->
|
||||
<!-- <el-button-->
|
||||
<!-- v-else-->
|
||||
<!-- @click="openSpmUrl(`${item.domain}`,item,item.tenantId,true)"-->
|
||||
<!-- >-->
|
||||
<!-- {{ item.type }}-->
|
||||
<!-- </el-button>-->
|
||||
<!-- </template>-->
|
||||
</el-space>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {FullScreen} from '@element-plus/icons-vue'
|
||||
import Breadcrumb from "~/components/Breadcrumb.vue";
|
||||
import type {ApiResult} from "~/api";
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
import {loginAdminByToken, loginDeveloperCenterByToken, openSpmUrl} from "~/utils/common";
|
||||
|
||||
const token = useToken();
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
title?: string;
|
||||
desc?: string;
|
||||
buyUrl?: string;
|
||||
form?: Company;
|
||||
value?: number;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void
|
||||
}>()
|
||||
|
||||
const onBuy = (item: Company) => {
|
||||
// if(item.type === 1){
|
||||
// // 插件
|
||||
// openSpmUrl(`/product/checkout`,item,item.productId)
|
||||
// }else {
|
||||
// // 产品
|
||||
// openSpmUrl(`/product/create`,item,item.productId)
|
||||
// }
|
||||
if (!token.value || token.value == '') {
|
||||
ElMessage.error('请先登录');
|
||||
setTimeout(() => {
|
||||
openSpmUrl(`/product/create`, item, item.companyId)
|
||||
}, 500)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 安装插件
|
||||
const installPlug = () => {
|
||||
const loading = ElLoading.service({
|
||||
lock: true,
|
||||
text: '安装中...'
|
||||
})
|
||||
useClientRequest<ApiResult<any>>(`/system/menu/install`, {
|
||||
method: 'POST',
|
||||
body: {
|
||||
companyId: getIdBySpm(5)
|
||||
}
|
||||
}).then(res => {
|
||||
if (res.code === 0) {
|
||||
setTimeout(() => {
|
||||
ElMessage.success(res.message);
|
||||
loading.close()
|
||||
emit('done')
|
||||
}, 500)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.rounded-avatar {
|
||||
border-radius: 30px;
|
||||
}
|
||||
|
||||
.rounded-avatar-xs {
|
||||
border-radius: 20px;
|
||||
}
|
||||
</style>
|
||||
293
pages/item/index.vue
Normal file
293
pages/item/index.vue
Normal file
@@ -0,0 +1,293 @@
|
||||
<!-- 文章详情 -->
|
||||
<template>
|
||||
<PageBanner :form="form" @done="reload"/>
|
||||
|
||||
<div class="page-main md:w-screen-xl m-auto p-3">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="18" :xs="24">
|
||||
<el-card shadow="hover" class="hover:border-green-50 hover:border-2 mb-5">
|
||||
<template #header>
|
||||
<div class="card-header font-bold text-xl">
|
||||
<span>应用介绍</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-descriptions title="应用参数" :column="2" border>
|
||||
<el-descriptions-item :span="2" label="应用名称">{{form.tenantName}}</el-descriptions-item>
|
||||
<el-descriptions-item v-if="form.isBuy" label="租户ID"><span class="text-orange-500">{{form.tenantId}}</span></el-descriptions-item>
|
||||
<el-descriptions-item v-if="form.isBuy" label="插件ID"><span class="text-orange-500">{{form.menuId || '-'}}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="控制台"><a class="cursor-pointer" @click="openUrl(`https://${form.domain}`)">{{form.domain}}</a></el-descriptions-item>
|
||||
<el-descriptions-item v-for="(item,index) in form.parameters" :key="index" :label="item.name">{{ item.value }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<template v-if="form.accounts && form.accounts.length > 0">
|
||||
<div class="h-[24px]"></div>
|
||||
<el-descriptions title="登录账号" :column="1" border>
|
||||
<template v-for="(item,index) in form.accounts" :key="index">
|
||||
<el-descriptions-item :label="item.type" v-if="item.account">
|
||||
还没有账号? <el-button type="text" @click="openSpmUrl(`/passport/regis`)">立即注册</el-button>
|
||||
</el-descriptions-item>
|
||||
</template>
|
||||
</el-descriptions>
|
||||
</template>
|
||||
<template v-if="form.gits && form.gits.length > 0">
|
||||
<div class="h-[24px]"></div>
|
||||
<el-descriptions title="代码仓库" :column="1" border>
|
||||
<el-descriptions-item v-for="(item,index) in form.gits" :key="index" :label="item.title">
|
||||
<el-input v-model="item.domain" readonly />
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</template>
|
||||
<template v-if="form.content">
|
||||
<div class="h-[24px]"></div>
|
||||
<el-descriptions title="详细说明" />
|
||||
<p v-html="form.content" class="content"></p>
|
||||
</template>
|
||||
<template v-if="form.files && form.files.length > 0">
|
||||
<div class="h-[24px]"></div>
|
||||
<el-descriptions title="应用截图" />
|
||||
<div v-for="(item,index) in form.files" :key="index" class="text item">
|
||||
<el-image
|
||||
:src="item.url"
|
||||
:zoom-rate="1.2"
|
||||
:max-scale="7"
|
||||
:min-scale="0.2"
|
||||
:preview-src-list="srcList"
|
||||
:initial-index="4"
|
||||
fit="contain"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</el-card>
|
||||
<!-- <el-card shadow="hover" v-if="form.files?.length" class="hover:border-green-50 hover:border-2 mb-5">-->
|
||||
<!-- <template #header>-->
|
||||
<!-- <div class="card-header font-bold text-xl">-->
|
||||
<!-- <span>应用截图</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- </template>-->
|
||||
<!-- <div class="flex gap-xl">-->
|
||||
<!-- <template v-for="(item,index) in form.files" :key="index" class="text item">-->
|
||||
<!-- <el-image-->
|
||||
<!-- :src="item.url"-->
|
||||
<!-- :zoom-rate="1.2"-->
|
||||
<!-- :max-scale="7"-->
|
||||
<!-- :min-scale="0.2"-->
|
||||
<!-- :preview-src-list="srcList"-->
|
||||
<!-- :initial-index="4"-->
|
||||
<!-- fit="contain"-->
|
||||
<!-- />-->
|
||||
<!-- </template>-->
|
||||
<!-- </div>-->
|
||||
<!-- </el-card>-->
|
||||
<!-- 产品评论 -->
|
||||
<Comments :productId="form.companyId" :comments="comments" :count="commentsTotal" @done="doComments" />
|
||||
</el-col>
|
||||
<el-col :span="6" :xs="24">
|
||||
<el-card shadow="hover" class="hover:border-green-50 hover:border-2 mb-5">
|
||||
<template #header>
|
||||
<div class="card-header font-bold text-xl">
|
||||
<span>开发者信息</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-space class="flex items-center">
|
||||
<div class="avatar">
|
||||
<el-avatar :size="55" :src="form.companyLogo"/>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="font-bold text-lg text-gray-600">{{ form.companyName }}</span>
|
||||
<span class="text-gray-400 pb-1 line-clamp-2">{{ form.comments }}</span>
|
||||
</div>
|
||||
</el-space>
|
||||
<div class="flex flex-col text-gray-500 justify-between leading-7 mt-3">
|
||||
<el-space class="flex items-center"><el-icon><Download /></el-icon>下载:6</el-space>
|
||||
<el-space class="flex items-center"><el-icon><Star /></el-icon>收藏:0</el-space>
|
||||
<el-space class="flex items-center"><el-icon><Coin /></el-icon>赞赏:0</el-space>
|
||||
<el-space class="flex items-center"><el-icon><Tickets /></el-icon>文档:0</el-space>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {Cpu,Download,Star,Coin,Tickets} from '@element-plus/icons-vue'
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import type {BreadcrumbItem} from "~/types/global";
|
||||
import {getIdBySpm, openUrl} from "~/utils/common";
|
||||
import useFormData from "~/utils/use-form-data";
|
||||
import PageBanner from './components/PageBanner.vue';
|
||||
import Comments from './components/Comments.vue';
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
import type {CompanyComment} from "~/api/system/companyComment/model";
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
const website = useWebsite();
|
||||
const breadcrumb = ref<BreadcrumbItem>();
|
||||
const comments = ref<CompanyComment[]>([]);
|
||||
const commentsTotal = ref(0);
|
||||
const commentsPage = ref(1);
|
||||
const activeName = ref();
|
||||
const url =
|
||||
'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg'
|
||||
const srcList = ref<any[]>([]);
|
||||
|
||||
// 配置信息
|
||||
const {form, assignFields} = useFormData<Company>({
|
||||
companyId: undefined,
|
||||
type: undefined,
|
||||
shortName: undefined,
|
||||
companyName: undefined,
|
||||
companyType: undefined,
|
||||
companyTypeMultiple: undefined,
|
||||
appType: undefined,
|
||||
companyLogo: undefined,
|
||||
image: undefined,
|
||||
files: undefined,
|
||||
content: undefined,
|
||||
companyCode: undefined,
|
||||
domain: undefined,
|
||||
phone: undefined,
|
||||
tel: undefined,
|
||||
email: undefined,
|
||||
InvoiceHeader: undefined,
|
||||
startTime: undefined,
|
||||
expirationTime: undefined,
|
||||
version: undefined,
|
||||
versionName: undefined,
|
||||
versionCode: undefined,
|
||||
members: undefined,
|
||||
storage: undefined,
|
||||
storageMax: undefined,
|
||||
buys: undefined,
|
||||
clicks: undefined,
|
||||
users: undefined,
|
||||
departments: undefined,
|
||||
industryParent: undefined,
|
||||
industryChild: undefined,
|
||||
country: undefined,
|
||||
province: undefined,
|
||||
city: undefined,
|
||||
region: undefined,
|
||||
address: undefined,
|
||||
latitude: undefined,
|
||||
longitude: undefined,
|
||||
businessEntity: undefined,
|
||||
comments: undefined,
|
||||
authentication: undefined,
|
||||
industryId: undefined,
|
||||
industryName: undefined,
|
||||
status: undefined,
|
||||
userId: undefined,
|
||||
official: undefined,
|
||||
price: undefined,
|
||||
planId: undefined,
|
||||
sortNumber: undefined,
|
||||
authoritative: undefined,
|
||||
menuId: undefined,
|
||||
merchantId: undefined,
|
||||
tenantId: undefined,
|
||||
tenantName: undefined,
|
||||
tenantCode: undefined,
|
||||
modules: undefined,
|
||||
requestUrl: undefined,
|
||||
socketUrl: undefined,
|
||||
serverUrl: undefined,
|
||||
modulesUrl: undefined,
|
||||
merchantUrl: undefined,
|
||||
websiteUrl: undefined,
|
||||
mpWeixinCode: undefined,
|
||||
mpAlipayCode: undefined,
|
||||
h5Code: undefined,
|
||||
androidUrl: undefined,
|
||||
iosUrl: undefined,
|
||||
avatar: undefined,
|
||||
nickname: undefined,
|
||||
code: undefined,
|
||||
createTime: undefined,
|
||||
updateTime: undefined,
|
||||
password: undefined,
|
||||
password2: undefined,
|
||||
collection: undefined,
|
||||
recommend: undefined,
|
||||
title: undefined,
|
||||
parentName: undefined,
|
||||
categoryName: undefined,
|
||||
parameters: undefined,
|
||||
links: undefined,
|
||||
accounts: undefined,
|
||||
gits: undefined,
|
||||
isBuy: undefined,
|
||||
installed: undefined
|
||||
});
|
||||
const doComments = async (page: any) => {
|
||||
commentsPage.value = page;
|
||||
await reloadComments();
|
||||
}
|
||||
|
||||
|
||||
// 加载评论
|
||||
const reloadComments = async () => {
|
||||
const {data: commentsResponse} = await useServerRequest<ApiResult<PageResult<CompanyComment>>>('/system/company-comment/page', {
|
||||
params: {
|
||||
companyId: getIdBySpm(5),
|
||||
page: commentsPage.value,
|
||||
// status: 1
|
||||
}
|
||||
})
|
||||
if(commentsResponse.value && commentsResponse.value?.data){
|
||||
comments.value = commentsResponse.value?.data?.list
|
||||
commentsTotal.value = commentsResponse.value?.data?.count;
|
||||
}
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
|
||||
// 存在spm(优先级高)
|
||||
const {data: item} = await useServerRequest<ApiResult<Company>>('/system/company/' + getIdBySpm(5))
|
||||
if (item.value?.data) {
|
||||
assignFields(item.value.data)
|
||||
form.title = item.value?.data?.title;
|
||||
form.parentName = '产品';
|
||||
form.categoryName = '产品详情';
|
||||
if (item.value.data.files) {
|
||||
form.files = JSON.parse(item.value?.data?.files)
|
||||
srcList.value = form.files?.map(d => d.url)
|
||||
}
|
||||
form.comments = item.value?.data?.comments;
|
||||
}
|
||||
|
||||
await reloadComments();
|
||||
|
||||
// seo
|
||||
useHead({
|
||||
title: `${form.tenantName} - ${website.value.websiteName}`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
// 面包屑
|
||||
breadcrumb.value = form
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
console.log(path, '=>Path')
|
||||
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.content {
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
358
pages/manage/cms/index.vue
Normal file
358
pages/manage/cms/index.vue
Normal file
@@ -0,0 +1,358 @@
|
||||
<template>
|
||||
<div class="common-layout">
|
||||
<el-container>
|
||||
<!-- 顶部菜单 -->
|
||||
<el-header class="bg-black flex items-center justify-between" height="50px">
|
||||
<el-space class="flex items-center">
|
||||
<el-avatar src="https://oss.wsdns.cn/20240331/7f7f7f57e12c45338beb7bfc7ecccfe9.png" class="cursor-pointer" shape="square" :size="32" @click="navigateTo('/manage', { replace: false })" />
|
||||
<div class="expand flex items-center justify-center block hover:bg-gray-700 h-[32px] w-[32px] cursor-pointer" @click="visible = !visible"><el-icon color="white" class="block" size="20"><Grid /></el-icon></div>
|
||||
</el-space>
|
||||
<template v-if="token">
|
||||
<el-dropdown @command="handleCommand">
|
||||
<el-avatar class="cursor-pointer" :src="userInfo?.avatar" :size="30" />
|
||||
<el-button circle :icon="ElIconUserFilled" color="#155FAA"></el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="user">个人中心</el-dropdown-item>
|
||||
<el-dropdown-item divided command="logOut" @click="navigateTo('/user/logout')">退出</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-header>
|
||||
<!-- 主体 -->
|
||||
<el-container>
|
||||
<!-- 左侧菜单区域 -->
|
||||
<el-aside width="200px" class="bg-white" :style="{ minHeight: 'calc(100vh - 50px)' }">
|
||||
<div class="flex items-center justify-between py-4 px-5 text-lg font-bold text-center bg-white border-b-1 border-b-gray-100 border-b-solid">
|
||||
企业官网 WDS
|
||||
<el-icon><Memo /></el-icon>
|
||||
</div>
|
||||
<el-menu
|
||||
default-active="2"
|
||||
class="el-menu-vertical-demo"
|
||||
style="border: none"
|
||||
@open="handleOpen"
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-menu-item index="1">
|
||||
<span>概况</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="2">
|
||||
<span>栏目管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="3">
|
||||
<span>文章列表</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="4">
|
||||
<span>素材管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="5">
|
||||
<span>广告管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="6">
|
||||
<span>订单管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="7">
|
||||
<span>用户管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="8">
|
||||
<span>系统设置</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</el-aside>
|
||||
<!-- 左侧内容区域 -->
|
||||
<el-container>
|
||||
<el-main class="overflow-y-hidden relative">
|
||||
<div class="absolute inset-0 bg-black opacity-10 z-1" v-if="visible" @click="visible = false"></div>
|
||||
<div v-if="visible" class="drawer w-screen-lg h-full bg-white left-0 top-[50px] fixed z-2 shadow-lg">
|
||||
<div class="flex items-center justify-between p-3 border-b border-b-solid border-b-gray-200">
|
||||
<div class="text-lg font-bold">产品与服务</div>
|
||||
<el-space class="flex items-center">
|
||||
<el-input
|
||||
class="w-20"
|
||||
placeholder="站内搜索"
|
||||
:suffix-icon="ElIconSearch"
|
||||
v-model="form.comments"
|
||||
@keyup.enter.native="handleSearch"
|
||||
/>
|
||||
<!-- <el-input v-model="search" placeholder="搜索应用" class="w-[200px]" />-->
|
||||
<div class="cursor-pointer mt-1 ml-5" @click="visible = false"><el-icon size="24" color="gray"><Close /></el-icon></div>
|
||||
</el-space>
|
||||
</div>
|
||||
<div class="menu-content flex">
|
||||
<div class="w-[200px] bg-gray-50">
|
||||
<el-menu
|
||||
default-active="2"
|
||||
class="el-menu-vertical-demo"
|
||||
style="border-right: 1px solid #f9fafb"
|
||||
@open="handleOpen"
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-menu-item index="2">
|
||||
<span>精选</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="3">
|
||||
<span>网页</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="4">
|
||||
<span>移动</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="5">
|
||||
<span>办公</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="6">
|
||||
<span>其他</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</div>
|
||||
<div class="w-full p-3">
|
||||
<el-scrollbar class="w-full" height="calc(100vh - 108px)">
|
||||
<el-row :gutter="16">
|
||||
<template v-for="item in 20" :key="item">
|
||||
<el-col :span="8">
|
||||
<div class="px-3 py-1 mb-2 text-sm text-gray-700 font-bold flex justify-between items-center">API 与工具</div>
|
||||
<p class="scrollbar-demo-item hover:bg-gray-100 px-3 py-1 mb-2 cursor-pointer text-sm text-gray-500 flex justify-between items-center">
|
||||
<span class="product-name">{{ item }} 云服务器 ECS</span>
|
||||
<span class="icon flex items-center hover:flex">
|
||||
<el-icon color="gray"><Star /></el-icon>
|
||||
</span>
|
||||
</p>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="my-3">
|
||||
<el-alert type="warning">
|
||||
<template #title>
|
||||
<text type="text" class="cursor-pointer">关于《小程序技术平台服务协议》更新公告</text>
|
||||
</template>
|
||||
</el-alert>
|
||||
</div>
|
||||
<el-card shadow="hover" class="flash mt-4 bg-white hover:shadow rounded-lg">
|
||||
<template #header>我的应用</template>
|
||||
<el-row :gutter="16">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :span="6" @click="openSpmUrl(`https://${item.tenantId}.websoft.top/token-login`,item,item.tenantId)">
|
||||
<div class="app-item block border-solid rounded-lg border-gray-300 border-1 bg-white mb-4 p-3 flex flex-row items-center hover:border-blue-4 hover:border-1.5 cursor-pointer">
|
||||
<el-avatar :src="item?.logo" shape="square" />
|
||||
<div class="info ml-2">
|
||||
<div class="app-item-title font-bold flex items-center">{{ item.tenantName }}<el-tag type="warning" class="ml-2" size="small">网页</el-tag></div>
|
||||
<div class="app-item-desc text-gray-400">{{ item.comments }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<!-- <el-card shadow="hover" class="mt-4">-->
|
||||
<!-- <el-tabs v-model="activeIndex" type="">-->
|
||||
<!-- <el-tab-pane label="微信小程序" name="mp-weixin">-->
|
||||
<!-- <el-table :data="tableData" stripe style="width: 100%">-->
|
||||
<!-- <el-table-column prop="date" label="Date" width="180" />-->
|
||||
<!-- <el-table-column prop="name" label="Name" width="180" />-->
|
||||
<!-- <el-table-column prop="address" label="Address" />-->
|
||||
<!-- </el-table>-->
|
||||
<!-- </el-tab-pane>-->
|
||||
<!-- <el-tab-pane label="网页/移动应用" name="web">-->
|
||||
<!-- </el-tab-pane>-->
|
||||
<!-- </el-tabs>-->
|
||||
<!-- </el-card>-->
|
||||
<el-card shadow="hover" class="flash mt-4 bg-white hover:shadow ">
|
||||
<template #header>开发工具推荐</template>
|
||||
<el-row :gutter="16">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :span="6" @click="openSpmUrl(`https://${item.tenantId}.websoft.top/token-login`,item,item.tenantId)">
|
||||
<div class="app-item block border-solid rounded-lg border-gray-300 border-1 mb-4 p-3 flex flex-row items-center hover:border-blue-4 hover:border-1.5 cursor-pointer">
|
||||
<div class="info ml-2">
|
||||
<div class="app-item-title">{{ item.tenantName }}</div>
|
||||
<div class="app-item-desc text-gray-400">{{ item.comments }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</el-main>
|
||||
<el-footer class="fixed bottom-0 items-center flex justify-center text-gray-400" :style="{ width: 'calc(100vw - 200px)' }">
|
||||
<span>Copyright © {{ new Date().getFullYear() }} {{ config?.copyright }}</span>
|
||||
</el-footer>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {useConfigInfo, useToken, useWebsite} from "~/composables/configState";
|
||||
import useFormData from '@/utils/use-form-data';
|
||||
import type { User } from '@/api/system/user/model';
|
||||
import { ref } from 'vue'
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
import type {Tenant} from "~/api/system/tenant/model";
|
||||
|
||||
// 配置信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const route = useRoute();
|
||||
const website = useWebsite()
|
||||
const config = useConfigInfo();
|
||||
const token = useToken();
|
||||
const userInfo = ref<User>();
|
||||
const visible = ref(false);
|
||||
const showIcon = ref(false);
|
||||
const activeIndex = ref('mp-weixin');
|
||||
const list = ref<Tenant[]>();
|
||||
const avatar = ref(localStorage.getItem('avatar'))
|
||||
|
||||
|
||||
import {
|
||||
Document,
|
||||
Menu as IconMenu,
|
||||
Location,
|
||||
Expand,
|
||||
MoreFilled,
|
||||
Grid,
|
||||
Memo,
|
||||
Close,
|
||||
Star,
|
||||
Setting,
|
||||
} from '@element-plus/icons-vue'
|
||||
import {navigateTo} from "#imports";
|
||||
const handleOpen = (key: string, keyPath: string[]) => {
|
||||
console.log(key, keyPath)
|
||||
}
|
||||
const handleClose = (key: string, keyPath: string[]) => {
|
||||
console.log(key, keyPath)
|
||||
}
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
]
|
||||
|
||||
// 配置信息
|
||||
const { form, assignFields } = useFormData<User>({
|
||||
userId: undefined,
|
||||
nickname: '',
|
||||
username: '',
|
||||
phone: '',
|
||||
mobile: '',
|
||||
sex: '',
|
||||
sexName: '',
|
||||
email: '',
|
||||
password: '',
|
||||
code: '',
|
||||
smsCode: '',
|
||||
comments: '',
|
||||
remember: true
|
||||
});
|
||||
|
||||
useHead({
|
||||
title: `用户中心 - ${config.value?.siteName}`,
|
||||
meta: [{ name: website.value.keywords, content: website.value.comments }]
|
||||
});
|
||||
|
||||
|
||||
function handleCommand(command: string) {
|
||||
switch (command) {
|
||||
case 'logOut':
|
||||
logOut();
|
||||
break;
|
||||
default:
|
||||
navigateTo('/user');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function logOut() {
|
||||
token.value = ''
|
||||
navigateTo('/passport/login')
|
||||
}
|
||||
|
||||
const onDone = (index: string) => {
|
||||
activeIndex.value = index;
|
||||
}
|
||||
|
||||
const onSubmit = async () => {
|
||||
const {data: modify } = await useServerRequest<ApiResult<User>>('/auth/user',{
|
||||
baseURL: runtimeConfig.public.apiServer,
|
||||
method: 'put',
|
||||
body: form
|
||||
})
|
||||
if(modify.value?.code == 0){
|
||||
ElMessage.success('修改成功')
|
||||
}
|
||||
}
|
||||
|
||||
const handleSearch = () => {
|
||||
console.log('搜索')
|
||||
}
|
||||
|
||||
|
||||
const getUserInfo = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<User>>('/auth/user',{baseURL: runtimeConfig.public.apiServer})
|
||||
if(response.value?.data){
|
||||
userInfo.value = response.value?.data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const reload = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Tenant>>>('/system/tenant/page',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
userId: localStorage.getItem('UserId')
|
||||
}})
|
||||
if(response.value?.data){
|
||||
console.log(response.value,'tenantList')
|
||||
if (response.value?.data.list) {
|
||||
list.value = response.value?.data.list;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
reload();
|
||||
getUserInfo();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
<style lang="less">
|
||||
.el-row {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.el-row:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.el-col {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.grid-content {
|
||||
border-radius: 4px;
|
||||
min-height: 36px;
|
||||
}
|
||||
</style>
|
||||
373
pages/manage/index.vue
Normal file
373
pages/manage/index.vue
Normal file
@@ -0,0 +1,373 @@
|
||||
<template>
|
||||
<div class="common-layout">
|
||||
<el-container>
|
||||
<!-- 顶部菜单 -->
|
||||
<el-header class="bg-black flex items-center justify-between" height="50px">
|
||||
<el-space class="flex items-center">
|
||||
<el-avatar src="https://oss.wsdns.cn/20240331/7f7f7f57e12c45338beb7bfc7ecccfe9.png" class="cursor-pointer" shape="square" :size="32" @click="navigateTo('/manage', { replace: false })" />
|
||||
<div class="expand flex items-center justify-center block hover:bg-gray-700 h-[32px] w-[32px] cursor-pointer" @click="visible = !visible"><el-icon color="white" class="block" size="20"><Grid /></el-icon></div>
|
||||
</el-space>
|
||||
<template v-if="token">
|
||||
<el-dropdown @command="handleCommand">
|
||||
<el-avatar class="cursor-pointer" :src="userInfo?.avatar" :size="30" />
|
||||
<el-button circle :icon="ElIconUserFilled" color="#155FAA"></el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="user">个人中心</el-dropdown-item>
|
||||
<el-dropdown-item divided command="logOut" @click="navigateTo('/user/logout')">退出</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-header>
|
||||
<!-- 主体 -->
|
||||
<el-container>
|
||||
<!-- 左侧菜单区域 -->
|
||||
<el-aside width="200px" class="bg-white" :style="{ minHeight: 'calc(100vh - 50px)' }">
|
||||
<div class="flex items-center justify-between py-4 px-5 text-lg font-bold text-center bg-white border-b-1 border-b-gray-100 border-b-solid">
|
||||
企业官网 WDS
|
||||
<el-icon><Memo /></el-icon>
|
||||
</div>
|
||||
<el-menu
|
||||
default-active="2"
|
||||
class="el-menu-vertical-demo"
|
||||
style="border: none"
|
||||
@open="handleOpen"
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-menu-item v-for="(item,index) in menus" :key="item.menuId" :index="`${item.menuId}`">
|
||||
<span>{{ item.title }}</span>
|
||||
</el-menu-item>
|
||||
<!-- <el-menu-item index="1">-->
|
||||
<!-- <span>概况</span>-->
|
||||
<!-- </el-menu-item>-->
|
||||
<!-- <el-menu-item index="2">-->
|
||||
<!-- <span>栏目管理</span>-->
|
||||
<!-- </el-menu-item>-->
|
||||
<!-- <el-menu-item index="3">-->
|
||||
<!-- <span>文章列表</span>-->
|
||||
<!-- </el-menu-item>-->
|
||||
<!-- <el-menu-item index="4">-->
|
||||
<!-- <span>素材管理</span>-->
|
||||
<!-- </el-menu-item>-->
|
||||
<!-- <el-menu-item index="5">-->
|
||||
<!-- <span>广告管理</span>-->
|
||||
<!-- </el-menu-item>-->
|
||||
<!-- <el-menu-item index="6">-->
|
||||
<!-- <span>订单管理</span>-->
|
||||
<!-- </el-menu-item>-->
|
||||
<!-- <el-menu-item index="7">-->
|
||||
<!-- <span>用户管理</span>-->
|
||||
<!-- </el-menu-item>-->
|
||||
<!-- <el-menu-item index="8">-->
|
||||
<!-- <span>系统设置</span>-->
|
||||
<!-- </el-menu-item>-->
|
||||
</el-menu>
|
||||
</el-aside>
|
||||
<!-- 左侧内容区域 -->
|
||||
<el-container>
|
||||
<el-main class="overflow-y-hidden relative">
|
||||
<div class="absolute inset-0 bg-black opacity-10 z-1" v-if="visible" @click="visible = false"></div>
|
||||
<div v-if="visible" class="drawer w-screen-lg h-full bg-white left-0 top-[50px] fixed z-2 shadow-lg">
|
||||
<div class="flex items-center justify-between p-3 border-b border-b-solid border-b-gray-200">
|
||||
<div class="text-lg font-bold">产品与服务</div>
|
||||
<el-space class="flex items-center">
|
||||
<el-input
|
||||
class="w-20"
|
||||
placeholder="站内搜索"
|
||||
:suffix-icon="ElIconSearch"
|
||||
v-model="form.comments"
|
||||
@keyup.enter.native="handleSearch"
|
||||
/>
|
||||
<!-- <el-input v-model="search" placeholder="搜索应用" class="w-[200px]" />-->
|
||||
<div class="cursor-pointer mt-1 ml-5" @click="visible = false"><el-icon size="24" color="gray"><Close /></el-icon></div>
|
||||
</el-space>
|
||||
</div>
|
||||
<div class="menu-content flex">
|
||||
<div class="w-[200px] bg-gray-50">
|
||||
<el-menu
|
||||
default-active="2"
|
||||
class="el-menu-vertical-demo"
|
||||
style="border-right: 1px solid #f9fafb"
|
||||
@open="handleOpen"
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-menu-item index="2">
|
||||
<span>精选</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="3">
|
||||
<span>网页</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="4">
|
||||
<span>移动</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="5">
|
||||
<span>办公</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="6">
|
||||
<span>其他</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</div>
|
||||
<div class="w-full p-3">
|
||||
<el-scrollbar class="w-full" height="calc(100vh - 108px)">
|
||||
<el-row :gutter="16">
|
||||
<template v-for="item in 20" :key="item">
|
||||
<el-col :span="8">
|
||||
<div class="px-3 py-1 mb-2 text-sm text-gray-700 font-bold flex justify-between items-center">API 与工具</div>
|
||||
<p class="scrollbar-demo-item hover:bg-gray-100 px-3 py-1 mb-2 cursor-pointer text-sm text-gray-500 flex justify-between items-center">
|
||||
<span class="product-name">{{ item }} 云服务器 ECS</span>
|
||||
<span class="icon flex items-center hover:flex">
|
||||
<el-icon color="gray"><Star /></el-icon>
|
||||
</span>
|
||||
</p>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="my-3">
|
||||
<el-alert type="warning">
|
||||
<template #title>
|
||||
<text type="text" class="cursor-pointer" @click="openSpmUrl(`/detail`,{},729,true)">关于《小程序技术平台服务协议》更新公告</text>
|
||||
</template>
|
||||
</el-alert>
|
||||
</div>
|
||||
<el-card shadow="hover" class="flash mt-4 bg-white hover:shadow rounded-lg">
|
||||
<template #header>我的应用</template>
|
||||
<el-row :gutter="16">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :span="6" @click="openSpmUrl(`https://${item.tenantId}.websoft.top/token-login`,item,item.tenantId)">
|
||||
<div class="app-item block border-solid rounded-lg border-gray-300 border-1 bg-white mb-4 p-3 flex flex-row items-center hover:border-blue-4 hover:border-1.5 cursor-pointer">
|
||||
<el-avatar :src="item?.logo" shape="square" />
|
||||
<div class="info ml-2">
|
||||
<div class="app-item-title font-bold flex items-center">{{ item.tenantName }}<el-tag type="warning" class="ml-2" size="small">网页</el-tag></div>
|
||||
<div class="app-item-desc text-gray-400">{{ item.comments }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<!-- <el-card shadow="hover" class="mt-4">-->
|
||||
<!-- <el-tabs v-model="activeIndex" type="">-->
|
||||
<!-- <el-tab-pane label="微信小程序" name="mp-weixin">-->
|
||||
<!-- <el-table :data="tableData" stripe style="width: 100%">-->
|
||||
<!-- <el-table-column prop="date" label="Date" width="180" />-->
|
||||
<!-- <el-table-column prop="name" label="Name" width="180" />-->
|
||||
<!-- <el-table-column prop="address" label="Address" />-->
|
||||
<!-- </el-table>-->
|
||||
<!-- </el-tab-pane>-->
|
||||
<!-- <el-tab-pane label="网页/移动应用" name="web">-->
|
||||
<!-- </el-tab-pane>-->
|
||||
<!-- </el-tabs>-->
|
||||
<!-- </el-card>-->
|
||||
<el-card shadow="hover" class="flash mt-4 bg-white hover:shadow ">
|
||||
<template #header>开发工具推荐</template>
|
||||
<el-row :gutter="16">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :span="6" @click="openSpmUrl(`https://${item.tenantId}.websoft.top/token-login`,item,item.tenantId)">
|
||||
<div class="app-item block border-solid rounded-lg border-gray-300 border-1 mb-4 p-3 flex flex-row items-center hover:border-blue-4 hover:border-1.5 cursor-pointer">
|
||||
<div class="info ml-2">
|
||||
<div class="app-item-title">{{ item.tenantName }}</div>
|
||||
<div class="app-item-desc text-gray-400">{{ item.comments }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</el-main>
|
||||
<el-footer class="fixed bottom-0 items-center flex justify-center text-gray-400" :style="{ width: 'calc(100vw - 200px)' }">
|
||||
<span>Copyright © {{ new Date().getFullYear() }} {{ config?.copyright }}</span>
|
||||
</el-footer>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {useConfigInfo, useToken, useWebsite} from "~/composables/configState";
|
||||
import useFormData from '@/utils/use-form-data';
|
||||
import type { User } from '@/api/system/user/model';
|
||||
import { ref } from 'vue'
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
import type {Tenant} from "~/api/system/tenant/model";
|
||||
|
||||
// 配置信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const route = useRoute();
|
||||
const website = useWebsite()
|
||||
const config = useConfigInfo();
|
||||
const token = useToken();
|
||||
const userInfo = ref<User>();
|
||||
const visible = ref(false);
|
||||
const showIcon = ref(false);
|
||||
const activeIndex = ref('mp-weixin');
|
||||
const list = ref<Tenant[]>();
|
||||
const menus = ref<Menu[]>();
|
||||
const avatar = ref(localStorage.getItem('avatar'))
|
||||
|
||||
|
||||
import {
|
||||
Document,
|
||||
Menu as IconMenu,
|
||||
Location,
|
||||
Expand,
|
||||
MoreFilled,
|
||||
Grid,
|
||||
Memo,
|
||||
Close,
|
||||
Star,
|
||||
Setting,
|
||||
} from '@element-plus/icons-vue'
|
||||
import {navigateTo} from "#imports";
|
||||
import type {Menu} from "~/api/system/menu/model";
|
||||
const handleOpen = (key: string, keyPath: string[]) => {
|
||||
console.log(key, keyPath)
|
||||
}
|
||||
const handleClose = (key: string, keyPath: string[]) => {
|
||||
console.log(key, keyPath)
|
||||
}
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
]
|
||||
|
||||
// 配置信息
|
||||
const { form, assignFields } = useFormData<User>({
|
||||
userId: undefined,
|
||||
nickname: '',
|
||||
username: '',
|
||||
phone: '',
|
||||
mobile: '',
|
||||
sex: '',
|
||||
sexName: '',
|
||||
email: '',
|
||||
password: '',
|
||||
code: '',
|
||||
smsCode: '',
|
||||
comments: '',
|
||||
remember: true
|
||||
});
|
||||
|
||||
useHead({
|
||||
title: `用户中心 - ${config.value?.siteName}`,
|
||||
meta: [{ name: website.value.keywords, content: website.value.comments }]
|
||||
});
|
||||
|
||||
|
||||
function handleCommand(command: string) {
|
||||
switch (command) {
|
||||
case 'logOut':
|
||||
logOut();
|
||||
break;
|
||||
default:
|
||||
navigateTo('/user');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function logOut() {
|
||||
token.value = ''
|
||||
navigateTo('/passport/login')
|
||||
}
|
||||
|
||||
const onDone = (index: string) => {
|
||||
activeIndex.value = index;
|
||||
}
|
||||
|
||||
const onSubmit = async () => {
|
||||
const {data: modify } = await useServerRequest<ApiResult<User>>('/auth/user',{
|
||||
baseURL: runtimeConfig.public.apiServer,
|
||||
method: 'put',
|
||||
body: form
|
||||
})
|
||||
if(modify.value?.code == 0){
|
||||
ElMessage.success('修改成功')
|
||||
}
|
||||
}
|
||||
|
||||
const handleSearch = () => {
|
||||
console.log('搜索')
|
||||
}
|
||||
|
||||
|
||||
const getUserInfo = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<User>>('/auth/user',{baseURL: runtimeConfig.public.apiServer})
|
||||
if(response.value?.data){
|
||||
userInfo.value = response.value?.data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const reload = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Tenant>>>('/system/tenant/page',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
userId: localStorage.getItem('UserId')
|
||||
}})
|
||||
if(response.value?.data){
|
||||
console.log(response.value,'tenantList')
|
||||
if (response.value?.data.list) {
|
||||
list.value = response.value?.data.list;
|
||||
}
|
||||
}
|
||||
const {data: response2} = await useServerRequest<ApiResult<PageResult<Menu>>>('/system/menu',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
parentId: 64688,
|
||||
menuType: 0,
|
||||
hide: 0
|
||||
}})
|
||||
if(response2.value?.data){
|
||||
if (response2.value?.data) {
|
||||
menus.value = response2.value.data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
reload();
|
||||
getUserInfo();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
<style lang="less">
|
||||
.el-row {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.el-row:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.el-col {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.grid-content {
|
||||
border-radius: 4px;
|
||||
min-height: 36px;
|
||||
}
|
||||
</style>
|
||||
358
pages/manage/shop/index.vue
Normal file
358
pages/manage/shop/index.vue
Normal file
@@ -0,0 +1,358 @@
|
||||
<template>
|
||||
<div class="common-layout">
|
||||
<el-container>
|
||||
<!-- 顶部菜单 -->
|
||||
<el-header class="bg-black flex items-center justify-between" height="50px">
|
||||
<el-space class="flex items-center">
|
||||
<el-avatar src="https://oss.wsdns.cn/20240331/7f7f7f57e12c45338beb7bfc7ecccfe9.png" class="cursor-pointer" shape="square" :size="32" @click="navigateTo('/manage', { replace: false })" />
|
||||
<div class="expand flex items-center justify-center block hover:bg-gray-700 h-[32px] w-[32px] cursor-pointer" @click="visible = !visible"><el-icon color="white" class="block" size="20"><Grid /></el-icon></div>
|
||||
</el-space>
|
||||
<template v-if="token">
|
||||
<el-dropdown @command="handleCommand">
|
||||
<el-avatar class="cursor-pointer" :src="userInfo?.avatar" :size="30" />
|
||||
<el-button circle :icon="ElIconUserFilled" color="#155FAA"></el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="user">个人中心</el-dropdown-item>
|
||||
<el-dropdown-item divided command="logOut" @click="navigateTo('/user/logout')">退出</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-header>
|
||||
<!-- 主体 -->
|
||||
<el-container>
|
||||
<!-- 左侧菜单区域 -->
|
||||
<el-aside width="200px" class="bg-white" :style="{ minHeight: 'calc(100vh - 50px)' }">
|
||||
<div class="flex items-center justify-between py-4 px-5 text-lg font-bold text-center bg-white border-b-1 border-b-gray-100 border-b-solid">
|
||||
企业商城 EMall
|
||||
<el-icon><Memo /></el-icon>
|
||||
</div>
|
||||
<el-menu
|
||||
default-active="2"
|
||||
class="el-menu-vertical-demo"
|
||||
style="border: none"
|
||||
@open="handleOpen"
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-menu-item index="1">
|
||||
<span>概况</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="2">
|
||||
<span>栏目管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="3">
|
||||
<span>文章列表</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="4">
|
||||
<span>素材管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="5">
|
||||
<span>广告管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="6">
|
||||
<span>订单管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="7">
|
||||
<span>用户管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="8">
|
||||
<span>系统设置</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</el-aside>
|
||||
<!-- 左侧内容区域 -->
|
||||
<el-container>
|
||||
<el-main class="overflow-y-hidden relative">
|
||||
<div class="absolute inset-0 bg-black opacity-10 z-1" v-if="visible" @click="visible = false"></div>
|
||||
<div v-if="visible" class="drawer w-screen-lg h-full bg-white left-0 top-[50px] fixed z-2 shadow-lg">
|
||||
<div class="flex items-center justify-between p-3 border-b border-b-solid border-b-gray-200">
|
||||
<div class="text-lg font-bold">产品与服务</div>
|
||||
<el-space class="flex items-center">
|
||||
<el-input
|
||||
class="w-20"
|
||||
placeholder="站内搜索"
|
||||
:suffix-icon="ElIconSearch"
|
||||
v-model="form.comments"
|
||||
@keyup.enter.native="handleSearch"
|
||||
/>
|
||||
<!-- <el-input v-model="search" placeholder="搜索应用" class="w-[200px]" />-->
|
||||
<div class="cursor-pointer mt-1 ml-5" @click="visible = false"><el-icon size="24" color="gray"><Close /></el-icon></div>
|
||||
</el-space>
|
||||
</div>
|
||||
<div class="menu-content flex">
|
||||
<div class="w-[200px] bg-gray-50">
|
||||
<el-menu
|
||||
default-active="2"
|
||||
class="el-menu-vertical-demo"
|
||||
style="border-right: 1px solid #f9fafb"
|
||||
@open="handleOpen"
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-menu-item index="2">
|
||||
<span>精选</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="3">
|
||||
<span>网页</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="4">
|
||||
<span>移动</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="5">
|
||||
<span>办公</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="6">
|
||||
<span>其他</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</div>
|
||||
<div class="w-full p-3">
|
||||
<el-scrollbar class="w-full" height="calc(100vh - 108px)">
|
||||
<el-row :gutter="16">
|
||||
<template v-for="item in 20" :key="item">
|
||||
<el-col :span="8">
|
||||
<div class="px-3 py-1 mb-2 text-sm text-gray-700 font-bold flex justify-between items-center">API 与工具</div>
|
||||
<p class="scrollbar-demo-item hover:bg-gray-100 px-3 py-1 mb-2 cursor-pointer text-sm text-gray-500 flex justify-between items-center">
|
||||
<span class="product-name">{{ item }} 云服务器 ECS</span>
|
||||
<span class="icon flex items-center hover:flex">
|
||||
<el-icon color="gray"><Star /></el-icon>
|
||||
</span>
|
||||
</p>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="my-3">
|
||||
<el-alert type="warning">
|
||||
<template #title>
|
||||
<text type="text" class="cursor-pointer">关于《小程序技术平台服务协议》更新公告</text>
|
||||
</template>
|
||||
</el-alert>
|
||||
</div>
|
||||
<el-card shadow="hover" class="flash mt-4 bg-white hover:shadow rounded-lg">
|
||||
<template #header>我的应用</template>
|
||||
<el-row :gutter="16">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :span="6" @click="openSpmUrl(`https://${item.tenantId}.websoft.top/token-login`,item,item.tenantId)">
|
||||
<div class="app-item block border-solid rounded-lg border-gray-300 border-1 bg-white mb-4 p-3 flex flex-row items-center hover:border-blue-4 hover:border-1.5 cursor-pointer">
|
||||
<el-avatar :src="item?.logo" shape="square" />
|
||||
<div class="info ml-2">
|
||||
<div class="app-item-title font-bold flex items-center">{{ item.tenantName }}<el-tag type="warning" class="ml-2" size="small">网页</el-tag></div>
|
||||
<div class="app-item-desc text-gray-400">{{ item.comments }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<!-- <el-card shadow="hover" class="mt-4">-->
|
||||
<!-- <el-tabs v-model="activeIndex" type="">-->
|
||||
<!-- <el-tab-pane label="微信小程序" name="mp-weixin">-->
|
||||
<!-- <el-table :data="tableData" stripe style="width: 100%">-->
|
||||
<!-- <el-table-column prop="date" label="Date" width="180" />-->
|
||||
<!-- <el-table-column prop="name" label="Name" width="180" />-->
|
||||
<!-- <el-table-column prop="address" label="Address" />-->
|
||||
<!-- </el-table>-->
|
||||
<!-- </el-tab-pane>-->
|
||||
<!-- <el-tab-pane label="网页/移动应用" name="web">-->
|
||||
<!-- </el-tab-pane>-->
|
||||
<!-- </el-tabs>-->
|
||||
<!-- </el-card>-->
|
||||
<el-card shadow="hover" class="flash mt-4 bg-white hover:shadow ">
|
||||
<template #header>开发工具推荐</template>
|
||||
<el-row :gutter="16">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :span="6" @click="openSpmUrl(`https://${item.tenantId}.websoft.top/token-login`,item,item.tenantId)">
|
||||
<div class="app-item block border-solid rounded-lg border-gray-300 border-1 mb-4 p-3 flex flex-row items-center hover:border-blue-4 hover:border-1.5 cursor-pointer">
|
||||
<div class="info ml-2">
|
||||
<div class="app-item-title">{{ item.tenantName }}</div>
|
||||
<div class="app-item-desc text-gray-400">{{ item.comments }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</el-main>
|
||||
<el-footer class="fixed bottom-0 items-center flex justify-center text-gray-400" :style="{ width: 'calc(100vw - 200px)' }">
|
||||
<span>Copyright © {{ new Date().getFullYear() }} {{ config?.copyright }}</span>
|
||||
</el-footer>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {useConfigInfo, useToken, useWebsite} from "~/composables/configState";
|
||||
import useFormData from '@/utils/use-form-data';
|
||||
import type { User } from '@/api/system/user/model';
|
||||
import { ref } from 'vue'
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
import type {Tenant} from "~/api/system/tenant/model";
|
||||
|
||||
// 配置信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const route = useRoute();
|
||||
const website = useWebsite()
|
||||
const config = useConfigInfo();
|
||||
const token = useToken();
|
||||
const userInfo = ref<User>();
|
||||
const visible = ref(false);
|
||||
const showIcon = ref(false);
|
||||
const activeIndex = ref('mp-weixin');
|
||||
const list = ref<Tenant[]>();
|
||||
const avatar = ref(localStorage.getItem('avatar'))
|
||||
|
||||
|
||||
import {
|
||||
Document,
|
||||
Menu as IconMenu,
|
||||
Location,
|
||||
Expand,
|
||||
MoreFilled,
|
||||
Grid,
|
||||
Memo,
|
||||
Close,
|
||||
Star,
|
||||
Setting,
|
||||
} from '@element-plus/icons-vue'
|
||||
import {navigateTo} from "#imports";
|
||||
const handleOpen = (key: string, keyPath: string[]) => {
|
||||
console.log(key, keyPath)
|
||||
}
|
||||
const handleClose = (key: string, keyPath: string[]) => {
|
||||
console.log(key, keyPath)
|
||||
}
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: 'Tom',
|
||||
address: 'No. 189, Grove St, Los Angeles',
|
||||
},
|
||||
]
|
||||
|
||||
// 配置信息
|
||||
const { form, assignFields } = useFormData<User>({
|
||||
userId: undefined,
|
||||
nickname: '',
|
||||
username: '',
|
||||
phone: '',
|
||||
mobile: '',
|
||||
sex: '',
|
||||
sexName: '',
|
||||
email: '',
|
||||
password: '',
|
||||
code: '',
|
||||
smsCode: '',
|
||||
comments: '',
|
||||
remember: true
|
||||
});
|
||||
|
||||
useHead({
|
||||
title: `用户中心 - ${config.value?.siteName}`,
|
||||
meta: [{ name: website.value.keywords, content: website.value.comments }]
|
||||
});
|
||||
|
||||
|
||||
function handleCommand(command: string) {
|
||||
switch (command) {
|
||||
case 'logOut':
|
||||
logOut();
|
||||
break;
|
||||
default:
|
||||
navigateTo('/user');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function logOut() {
|
||||
token.value = ''
|
||||
navigateTo('/passport/login')
|
||||
}
|
||||
|
||||
const onDone = (index: string) => {
|
||||
activeIndex.value = index;
|
||||
}
|
||||
|
||||
const onSubmit = async () => {
|
||||
const {data: modify } = await useServerRequest<ApiResult<User>>('/auth/user',{
|
||||
baseURL: runtimeConfig.public.apiServer,
|
||||
method: 'put',
|
||||
body: form
|
||||
})
|
||||
if(modify.value?.code == 0){
|
||||
ElMessage.success('修改成功')
|
||||
}
|
||||
}
|
||||
|
||||
const handleSearch = () => {
|
||||
console.log('搜索')
|
||||
}
|
||||
|
||||
|
||||
const getUserInfo = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<User>>('/auth/user',{baseURL: runtimeConfig.public.apiServer})
|
||||
if(response.value?.data){
|
||||
userInfo.value = response.value?.data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const reload = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Tenant>>>('/system/tenant/page',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
userId: localStorage.getItem('UserId')
|
||||
}})
|
||||
if(response.value?.data){
|
||||
console.log(response.value,'tenantList')
|
||||
if (response.value?.data.list) {
|
||||
list.value = response.value?.data.list;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
reload();
|
||||
getUserInfo();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
<style lang="less">
|
||||
.el-row {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.el-row:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.el-col {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.grid-content {
|
||||
border-radius: 4px;
|
||||
min-height: 36px;
|
||||
}
|
||||
</style>
|
||||
86
pages/market/components/CardList.vue
Normal file
86
pages/market/components/CardList.vue
Normal file
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in data?.list" :key="index">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer" @click="openSpmUrl(`/item`, item,item.companyId,true)">
|
||||
<el-image
|
||||
:src="item.image"
|
||||
:fit="fit" :lazy="true" class="w-full md:h-[150px] h-[199px] cursor-pointer bg-gray-50"/>
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col gap-1.5">
|
||||
<div class="flex-1 text-xl cursor-pointer flex items-center">
|
||||
{{ item.shortName }}
|
||||
<el-tag v-if="item.chargingMethod === 0" size="small" type="success" class="text-white ml-2" effect="dark">免费</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500 line-clamp-2">{{ item.comments }}</div>
|
||||
</div>
|
||||
<div class="button-group flex justify-between items-center mt-3">
|
||||
<el-space class="flex items-end">
|
||||
<el-avatar size="small" :src="item.companyLogo" />
|
||||
<span class="text-gray-400 line-clamp-1 pr-2">{{ item.companyName }}</span>
|
||||
</el-space>
|
||||
<!-- <el-button @click.stop="loginDeveloperCenterByToken(item)">控制台</el-button>-->
|
||||
<!-- 已购买 -->
|
||||
<!-- <template v-if="item.isBuy">-->
|
||||
<!-- <el-button v-if="item.isBuy && item.installed" type="success" @click.stop="openSpmUrl(`https://${item.domain}`,item,item.companyId,true)">进入控制台</el-button>-->
|
||||
<!-- <el-button v-if="item.isBuy && !item.installed" type="primary" @click.stop="openSpmUrl(`/product/create`,item,item.companyId,true)">立即开通</el-button>-->
|
||||
<!-- </template>-->
|
||||
<!-- 未够买 -->
|
||||
<!-- <template v-if="!item.isBuy">-->
|
||||
<!-- <div class="flex items-center cursor-pointer" v-if="!item.isBuy" @click.stop="openSpmUrl(`/product/create`,item,item.companyId,true)">-->
|
||||
<!-- <div class="flex items-center text-red-600">-->
|
||||
<!-- <span>¥{{ item.price }}</span>-->
|
||||
<!-- <span v-if="item.chargingMethod == 2">/年</span>-->
|
||||
<!-- <span v-if="item.chargingMethod == 3">/月</span>-->
|
||||
<!-- <span v-if="item.chargingMethod == 4">/天</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </template>-->
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div class="flex justify-center py-3">
|
||||
<el-pagination background layout="prev, pager, next" :total="data?.count" @change="onPage" />
|
||||
</div>
|
||||
<!-- <div v-if="disabled" class="px-1 text-center text-gray-500">-->
|
||||
<!-- 没有更多了-->
|
||||
<!-- </div>-->
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
import type {PageResult} from "~/api";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
data?: PageResult<Company>;
|
||||
disabled?: boolean;
|
||||
fit?: any;
|
||||
}>(),
|
||||
{
|
||||
fit: 'cover'
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done', index: number): void;
|
||||
}>();
|
||||
|
||||
// const load = () => {
|
||||
// if(!props.disabled){
|
||||
// emit('done')
|
||||
// }
|
||||
// }
|
||||
|
||||
const onPage = (index: number) => {
|
||||
emit('done',index)
|
||||
}
|
||||
</script>
|
||||
75
pages/market/index.vue
Normal file
75
pages/market/index.vue
Normal file
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<PageBanner :layout="layout" :title="`${form?.categoryName}`" :desc="`${form?.comments}`" />
|
||||
<CardList :param="{type: 0,official: true}" :data="data" :disabled="disabled" @done="onSearch" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import type {Company, CompanyParam} from "~/api/system/company/model";
|
||||
import CardList from './components/CardList.vue';
|
||||
import {getIdBySpm} from "~/utils/common";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const data = ref<PageResult<Company>>();
|
||||
const page = ref<number>(0);
|
||||
const resultText = ref('');
|
||||
const layout = ref<any>();
|
||||
const disabled = ref<boolean>(false);
|
||||
|
||||
// 获取状态
|
||||
const form = ref<Navigation>();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
const onSearch = (index: number) => {
|
||||
page.value = index;
|
||||
reload();
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Company>>>('/system/company/page',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value,
|
||||
limit: 12,
|
||||
categoryId: getIdBySpm(5),
|
||||
keywords: where.keywords
|
||||
}})
|
||||
if(response.value?.data){
|
||||
data.value = response.value?.data;
|
||||
}
|
||||
}
|
||||
|
||||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>(`/cms/cms-navigation/${getIdBySpm(5)}`)
|
||||
if(nav.value?.data){
|
||||
form.value = nav.value?.data;
|
||||
useHead({
|
||||
title: `${form.value.title} - WEBSOFT`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
}
|
||||
// 页面布局
|
||||
if(form.value?.layout){
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
|
||||
|
||||
|
||||
watch(
|
||||
() => getIdBySpm(5),
|
||||
(id) => {
|
||||
console.log(id,'id = .>>>>')
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
82
pages/merchant/index.vue
Normal file
82
pages/merchant/index.vue
Normal file
@@ -0,0 +1,82 @@
|
||||
<template>
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useConfigInfo, useForm, useToken, useWebsite} from "~/composables/configState";
|
||||
import type {BreadcrumbItem} from "~/types/global";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import {getIdBySpm} from "~/utils/common";
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
const user = useUser();
|
||||
const layout = ref<any>();
|
||||
const config = useConfigInfo();
|
||||
const token = useToken();
|
||||
const form = useForm();
|
||||
const breadcrumb = ref<BreadcrumbItem>();
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
|
||||
// 存在spm(优先级高)
|
||||
const {data: nav} = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/' + getIdBySpm(5))
|
||||
if (nav.value?.data) {
|
||||
form.value = nav.value.data
|
||||
} else {
|
||||
const {data: nav} = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/getNavigationByPath', {query: {path: route.path}})
|
||||
if (nav.value?.data) {
|
||||
form.value = nav.value?.data;
|
||||
}
|
||||
}
|
||||
// 页面布局
|
||||
if (form.value?.layout) {
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
|
||||
// 未登录状态(是否强制登录)
|
||||
if (!token.value || token.value == '') {
|
||||
if (config.value.MustLogin) {
|
||||
navigateTo('/passport/login');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 判断是否开发者身份
|
||||
if(!user.value?.merchantId){
|
||||
navigateTo('/user/auth');
|
||||
return false;
|
||||
}
|
||||
// 开发者身份者跳转控制台
|
||||
if(user.value.merchantId){
|
||||
console.log('已认证未开发者>')
|
||||
}
|
||||
|
||||
// seo
|
||||
useHead({
|
||||
title: `现代Web应用开发(Vue)框架 · WEBSOFT`,
|
||||
meta: [{name: form.value.design?.keywords, content: form.value.design?.description}],
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
},
|
||||
script: [
|
||||
{
|
||||
children: `console.log(${JSON.stringify(form.value)})`,
|
||||
},
|
||||
],
|
||||
});
|
||||
// 面包屑
|
||||
breadcrumb.value = form.value
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
console.log(path, '=>Path')
|
||||
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
76
pages/page/index.vue
Normal file
76
pages/page/index.vue
Normal file
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<!-- Banner -->
|
||||
<Banner :layout="layout" />
|
||||
|
||||
<!-- 简单模式(常规单页面) -->
|
||||
<PageContainer :form="form" :layout="layout" />
|
||||
|
||||
<!-- 高级模式(自定义组件) -->
|
||||
<div class="flex flex-col" v-if="layout?.showLayout">
|
||||
{{ layout }}
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useConfigInfo, useForm, useToken, useWebsite} from "~/composables/configState";
|
||||
import type {BreadcrumbItem} from "~/types/global";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import {getIdBySpm} from "~/utils/common";
|
||||
import PageContainer from "~/components/PageContainer.vue";
|
||||
|
||||
// 引入状态管理
|
||||
const route = useRoute();
|
||||
const website = useWebsite();
|
||||
const layout = ref<any>();
|
||||
const config = useConfigInfo();
|
||||
const token = useToken();
|
||||
const form = useForm();
|
||||
const breadcrumb = ref<BreadcrumbItem>();
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
|
||||
// 存在spm(优先级高)
|
||||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/' + getIdBySpm(5))
|
||||
if (nav.value?.data) {
|
||||
form.value = nav.value.data
|
||||
}else{
|
||||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/getNavigationByPath',{query: {path: route.path}})
|
||||
if(nav.value?.data){
|
||||
form.value = nav.value?.data;
|
||||
}
|
||||
}
|
||||
// 页面布局
|
||||
if(form.value?.layout){
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
|
||||
// seo
|
||||
useHead({
|
||||
title: `${form.value.title} - ${website.value.websiteName}`,
|
||||
meta: [{ name: form.value.design?.keywords, content: form.value.design?.description }],
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
},
|
||||
script: [
|
||||
{
|
||||
children: `console.log(${JSON.stringify(form.value)})`,
|
||||
},
|
||||
],
|
||||
});
|
||||
// 面包屑
|
||||
breadcrumb.value = form.value
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
console.log(path,'=>Path')
|
||||
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
312
pages/passport/components/Auth.vue
Normal file
312
pages/passport/components/Auth.vue
Normal file
@@ -0,0 +1,312 @@
|
||||
<template>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
v-loading="loading"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-position="top"
|
||||
class="w-full sm:py-2"
|
||||
size="large"
|
||||
status-icon
|
||||
>
|
||||
<el-tabs v-model="form.type" class="flash bg-white ml-0">
|
||||
<el-tab-pane label="个人认证"/>
|
||||
<el-tab-pane label="企业认证"/>
|
||||
</el-tabs>
|
||||
<!-- 已完成认证 -->
|
||||
<template v-if="form.status === 1">
|
||||
<template v-if="form.merchantCode == ''">
|
||||
<el-result
|
||||
icon="success"
|
||||
title="个人认证已通过"
|
||||
:sub-title="`认证完成时间 ${form.completedTime}`"
|
||||
>
|
||||
<template #extra>
|
||||
<el-button type="text" @click="onUpdate">修改认证信息</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-result
|
||||
icon="success"
|
||||
title="企业认证已通过"
|
||||
:sub-title="`认证完成时间 ${form.completedTime}`"
|
||||
>
|
||||
<template #extra>
|
||||
<el-button type="text" @click="onUpdate">修改认证信息</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
</template>
|
||||
</template>
|
||||
<!-- 申请被驳回 -->
|
||||
<template v-if="form.status === 2">
|
||||
<el-result
|
||||
icon="error"
|
||||
title="您的申请已被驳回"
|
||||
:sub-title="`${form.reason}`"
|
||||
>
|
||||
<template #extra>
|
||||
<el-button type="text" @click="onUpdate">修改认证信息</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
</template>
|
||||
<!-- 未完成认证 -->
|
||||
<template v-if="form.status === 0 && form.checkStatus">
|
||||
<el-result
|
||||
icon="warning"
|
||||
title="审核中"
|
||||
:sub-title="`您的申请已提交,请耐心等待工作人员的审核,非常感谢`"
|
||||
>
|
||||
</el-result>
|
||||
</template>
|
||||
<template v-if="form.status === 0 && !form.checkStatus">
|
||||
<template v-if="form.type == '1'">
|
||||
<el-form-item label="企业名称" prop="merchantName">
|
||||
<el-input v-model="form.merchantName" placeholder="请输入企业名称"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="社会信用代码" prop="merchantCode">
|
||||
<el-input v-model="form.merchantCode" placeholder="请输入社会信用代码"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="所属行业" prop="category">
|
||||
<el-cascader
|
||||
v-model="industry"
|
||||
:options="industryData"
|
||||
placeholder="请选择所属行业"
|
||||
class="w-full"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="LOGO">
|
||||
<el-upload
|
||||
v-model:file-list="fileList"
|
||||
:limit="1"
|
||||
class="upload-demo"
|
||||
action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
|
||||
:on-preview="handlePreview"
|
||||
:on-remove="handleRemove"
|
||||
list-type="picture"
|
||||
>
|
||||
<el-button size="default">上传文件</el-button>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
<el-form-item label="业务描述" prop="comments">
|
||||
<el-input v-model="form.comments" :rows="5" type="textarea" />
|
||||
</el-form-item>
|
||||
<el-form-item label="营业执照">
|
||||
<el-upload
|
||||
v-model:file-list="fileList"
|
||||
:limit="1"
|
||||
class="upload-demo"
|
||||
action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
|
||||
:on-preview="handlePreview"
|
||||
:on-remove="handleRemove"
|
||||
list-type="picture"
|
||||
>
|
||||
<el-button size="default">上传文件</el-button>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</template>
|
||||
<el-form-item label="真实姓名" prop="realName">
|
||||
<el-input v-model="form.realName" placeholder="请输入真实姓名"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="phone">
|
||||
<el-input v-model="form.phone" maxlength="11" placeholder="请输入真实有效的手机号码"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="证件号码" prop="idCard">
|
||||
<el-input v-model="form.idCard" placeholder="请输入证件号码"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="注册协议">
|
||||
<el-checkbox v-model="isAgree">
|
||||
请务必提供真实信息,我司有权自行或委托第三方审查您提供的身份信息是否属真实,有效。若提供虚假信息,由此的全部后果由您承担。
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-space class="flex">
|
||||
<el-button type="primary" size="large" :disabled="!isAgree" @click="submitForm(formRef)">
|
||||
{{ isUpdate ? '提交修改' : '提交申请' }}
|
||||
</el-button>
|
||||
</el-space>
|
||||
</template>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {reactive, ref} from 'vue'
|
||||
import {UploadFilled} from '@element-plus/icons-vue'
|
||||
import type {ComponentSize, FormInstance, FormRules, UploadProps, UploadUserFile} from 'element-plus'
|
||||
import type {ShopMerchantApply} from "~/api/shop/shopMerchantApply/model";
|
||||
import industryData from '@/assets/json/industry-data.json';
|
||||
import {useClientRequest} from "~/composables/useClientRequest";
|
||||
import type {ApiResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ShopMerchant} from "~/api/shop/shopMerchant/model";
|
||||
import useFormData from "~/utils/use-form-data";
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const fileList = ref<UploadUserFile[]>([])
|
||||
const industry = ref<any[]>([])
|
||||
const loading = ref<boolean>(true)
|
||||
const isUpdate = ref<boolean>(false)
|
||||
const showEdit = ref<boolean>(false)
|
||||
const isAgree = ref<boolean>(false)
|
||||
|
||||
const {form, assignFields, resetFields} = useFormData<ShopMerchantApply>({
|
||||
applyId: undefined,
|
||||
type: '0',
|
||||
merchantName: undefined,
|
||||
merchantCode: undefined,
|
||||
image: undefined,
|
||||
phone: undefined,
|
||||
realName: undefined,
|
||||
idCard: undefined,
|
||||
sfz1: undefined,
|
||||
sfz2: undefined,
|
||||
yyzz: undefined,
|
||||
name2: undefined,
|
||||
shopType: undefined,
|
||||
parentId: undefined,
|
||||
categoryId: undefined,
|
||||
category: undefined,
|
||||
commission: undefined,
|
||||
keywords: undefined,
|
||||
files: undefined,
|
||||
ownStore: undefined,
|
||||
recommend: undefined,
|
||||
completedTime: undefined,
|
||||
goodsReview: undefined,
|
||||
userId: undefined,
|
||||
comments: undefined,
|
||||
reason: undefined,
|
||||
checkStatus: undefined,
|
||||
status: 0,
|
||||
sortNumber: undefined,
|
||||
tenantId: undefined,
|
||||
createTime: undefined
|
||||
})
|
||||
|
||||
const rules = reactive<FormRules<ShopMerchantApply>>({
|
||||
realName: [
|
||||
{required: true, message: '请输入真实姓名', trigger: 'blur'},
|
||||
{min: 2, max: 5, message: '长度应为2-5个字符', trigger: 'blur'},
|
||||
],
|
||||
phone: [
|
||||
{required: true, message: '请输入手机号码', trigger: 'blur'},
|
||||
{pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur'},
|
||||
],
|
||||
idCard: [
|
||||
{required: true, message: '请输入证件号码', trigger: 'blur'},
|
||||
{min: 18, max: 18, message: '证件号码长度应为18位', trigger: 'blur'},
|
||||
],
|
||||
sfz1: [
|
||||
{required: true, message: '请上传身份证正面', trigger: 'change'}
|
||||
],
|
||||
sfz2: [
|
||||
{required: true, message: '请上传身份证反面', trigger: 'change'}
|
||||
],
|
||||
merchantName: [
|
||||
{required: true, message: '请输入企业名称', trigger: 'blur'}
|
||||
],
|
||||
merchantCode: [
|
||||
{required: true, message: '请输入社会信用代码', trigger: 'blur'}
|
||||
],
|
||||
yyzz: [
|
||||
{required: true, message: '请上传营业执照', trigger: 'change'}
|
||||
],
|
||||
category: [
|
||||
{required: true, message: '请选择所属行业', trigger: 'change'}
|
||||
]
|
||||
})
|
||||
|
||||
const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
|
||||
console.log(uploadFile, uploadFiles)
|
||||
}
|
||||
|
||||
const handlePreview: UploadProps['onPreview'] = (file) => {
|
||||
console.log(file)
|
||||
}
|
||||
|
||||
const handleClick = (index: number) => {
|
||||
// form.type = index
|
||||
}
|
||||
const props = {
|
||||
expandTrigger: 'hover' as const,
|
||||
}
|
||||
|
||||
// 所属行业
|
||||
const handleChange = (value: any) => {
|
||||
let parent = ''
|
||||
let category = ''
|
||||
industryData.map(d => {
|
||||
if (d.value == value[0]) {
|
||||
form.parentId = d.value
|
||||
parent = d.label
|
||||
if (d.children) {
|
||||
d.children.map(c => {
|
||||
if (c.value == value[1]) {
|
||||
category = c.label
|
||||
form.categoryId = c.value
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
form.category = `${parent}/${category}`
|
||||
}
|
||||
|
||||
const onUpdate = () => {
|
||||
form.status = 0;
|
||||
form.checkStatus = false
|
||||
}
|
||||
|
||||
const onEdit = () => {
|
||||
showEdit.value = !showEdit.value;
|
||||
}
|
||||
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid, fields) => {
|
||||
if (valid) {
|
||||
console.log('submit!')
|
||||
if (form.type == '0') {
|
||||
form.shopType = '个人开发者';
|
||||
}
|
||||
if(form.type == '1'){
|
||||
form.shopType = '企业开发者';
|
||||
}
|
||||
useClientRequest<ApiResult<any>>(`/shop/shop-merchant-apply`, {
|
||||
baseURL: runtimeConfig.public.apiServer,
|
||||
method: isUpdate.value ? 'PUT' : 'POST',
|
||||
body: form
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
ElMessage.success(res.message)
|
||||
reload();
|
||||
} else {
|
||||
return ElMessage.error(res.message)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
console.log('error submit!', fields)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const resetForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
resetFields();
|
||||
}
|
||||
|
||||
const reload = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<ShopMerchant>>('/shop/shop-merchant-apply/getByUserId', {baseURL: runtimeConfig.public.apiServer})
|
||||
if (response.value?.data) {
|
||||
console.log(response.value.data)
|
||||
isUpdate.value = true;
|
||||
assignFields(response.value.data)
|
||||
industry.value = []
|
||||
industry.value.push(form.parentId)
|
||||
industry.value.push(form.categoryId)
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
reload();
|
||||
</script>
|
||||
491
pages/passport/login.vue
Normal file
491
pages/passport/login.vue
Normal file
@@ -0,0 +1,491 @@
|
||||
<template>
|
||||
<div class="login flex justify-around py-24 h-[700px] items-center">
|
||||
<div class="flash">
|
||||
|
||||
</div>
|
||||
<el-card class="m-5 w-screen-sm sm:w-[430px] sm:h-[520px] flex justify-around relative border-0" style="border: 0;">
|
||||
<div class="login-bar absolute top-0 right-0 cursor-pointer" @click="onLoginBar">
|
||||
<div class="go-to-register cursor-pointer">
|
||||
<img src="https://img.alicdn.com/imgextra/i3/O1CN01yz6fEl1MwaRtkJyvf_!!6000000001499-55-tps-70-70.svg" alt=""/>
|
||||
</div>
|
||||
<span class="absolute top-3 right-1.5 text-sm text-white font-bold cursor-pointer">{{ loginBar ? '注册' : '登录' }}</span>
|
||||
</div>
|
||||
<!-- 登录界面 -->
|
||||
<el-space class="tabs pt-5 text-xl flex justify-center" v-if="loginBar">
|
||||
<el-tabs v-model="activeName" class="demo-tabs ">
|
||||
<el-tab-pane label="账号登录" name="account">
|
||||
<div class="custom-style my-4">
|
||||
<el-form :model="form" :rules="rules" ref="ruleForm" label-width="auto" class="w-[330px]">
|
||||
<!-- <el-form-item>-->
|
||||
<!-- <el-input class="w-full" size="large" placeholder="租户ID" :prefix-icon="Shop" v-model="form.tenantId" />-->
|
||||
<!-- </el-form-item>-->
|
||||
<el-form-item prop="username">
|
||||
<el-input class="w-full" size="large" placeholder="登录账号" :prefix-icon="Avatar" v-model="form.username" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="password">
|
||||
<el-input type="password" size="large" maxlength="100" placeholder="登录密码" :prefix-icon="Briefcase" v-model="form.password" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="code">
|
||||
<el-space class="flex justify-between w-full">
|
||||
<el-input size="large" placeholder="图形验证码" maxlength="5" v-model="form.code" @keyup.enter.prevent="onSubmit" />
|
||||
<el-image alt="" :src="captcha" @click="changeCaptcha" />
|
||||
</el-space>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="记住密码">
|
||||
<el-switch v-model="form.remember" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" size="large" class="w-full" @click="onSubmit">登录</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="短信登录" name="sms">
|
||||
<div class="custom-style my-4">
|
||||
<el-form :model="form" :rules="rules" ref="ruleForm" label-width="auto" class="w-[330px]">
|
||||
<el-form-item prop="phone">
|
||||
<el-input class="w-full" size="large" maxlength="11" placeholder="请输入手机号码" v-model="form.phone">
|
||||
<template #prepend>+86</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="code">
|
||||
<el-space class="flex justify-between w-full">
|
||||
<el-input size="large" placeholder="短信验证码" maxlength="6" class="w-full" v-model="form.code" @keyup.enter.prevent="onSubmitBySms" />
|
||||
<el-button size="large" class="w-full" :disabled="!!countdownTime" @click="checkUser">
|
||||
<span v-if="!countdownTime">发送验证码</span>
|
||||
<span v-else>已发送 {{ countdownTime }} s</span>
|
||||
</el-button>
|
||||
</el-space>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" size="large" class="w-full" @click="onSubmitBySms">登录</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-space>
|
||||
<!-- 快捷登录 --->
|
||||
<template v-if="loginBar && activeName == 'account'">
|
||||
<!-- <div class="clearfix flex justify-center">-->
|
||||
<!-- <el-divider>-->
|
||||
<!-- <span class="text-gray-400">其他登录方式</span>-->
|
||||
<!-- </el-divider>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="clearfix flex justify-center">-->
|
||||
<!-- <el-button circle :icon="ElIconUserFilled"></el-button>-->
|
||||
<!-- </div>-->
|
||||
</template>
|
||||
<!-- 注册界面 -->
|
||||
<el-space class="tabs pt-5 text-xl flex justify-center" v-if="!loginBar">
|
||||
<el-tabs v-model="activeName" class="demo-tabs ">
|
||||
<el-tab-pane label="注册账号" name="sms">
|
||||
<div class="custom-style ">
|
||||
<el-form :model="form" :rules="rules" ref="ruleForm" label-width="auto" class="w-[330px]">
|
||||
<el-form-item prop="phone">
|
||||
<span class="text-sm text-gray-400 mb-4">
|
||||
未注册手机号验证通过后将自动注册
|
||||
</span>
|
||||
<el-input class="w-full" size="large" maxlength="11" placeholder="请输入手机号码" v-model="form.phone">
|
||||
<template #prepend>+86</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="code">
|
||||
<el-space class="flex justify-between w-full">
|
||||
<el-input size="large" placeholder="短信验证码" maxlength="6" class="w-full" v-model="form.code" />
|
||||
<el-button size="large" class="w-full" :disabled="!!countdownTime" @click="checkUser">
|
||||
<span v-if="!countdownTime">发送验证码</span>
|
||||
<span v-else>已发送 {{ countdownTime }} s</span>
|
||||
</el-button>
|
||||
</el-space>
|
||||
</el-form-item>
|
||||
<el-form-item prop="companyName">
|
||||
<el-input class="w-full" size="large" placeholder="企业名称" v-model="form.companyName" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="email">
|
||||
<el-input class="w-full" size="large" placeholder="邮箱地址" v-model="form.email" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="form.isAgree">我已阅读并同意</el-checkbox>
|
||||
<a href="#" class="text-gray-700">《用户协议》</a>
|
||||
<a href="#" class="text-gray-700">《隐私政策》</a>
|
||||
<a href="#" class="text-gray-700">《产品服务协议》</a>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" size="large" class="w-full" :disabled="!form.isAgree" @click="onRegister">注册</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<!-- <el-tab-pane label="账号密码注册" name="account">-->
|
||||
<!-- <div class="custom-style my-4">-->
|
||||
<!-- <el-form :model="form" label-width="auto" class="w-[330px]">-->
|
||||
<!-- <el-form-item>-->
|
||||
<!-- <el-input class="w-full" size="large" maxlength="30" placeholder="登录账号" v-model="form.username" />-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item>-->
|
||||
<!-- <el-input type="password" size="large" maxlength="30" placeholder="登录密码" v-model="form.password" />-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item>-->
|
||||
<!-- <el-input class="w-full" size="large" maxlength="11" placeholder="请输入手机号码" v-model="form.phone">-->
|
||||
<!-- <template #prepend>+86</template>-->
|
||||
<!-- </el-input>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item>-->
|
||||
<!-- <el-space class="flex justify-between w-full">-->
|
||||
<!-- <el-input size="large" placeholder="短信验证码" maxlength="6" class="w-full" v-model="form.code" @keyup.enter.prevent="onSubmitBySms" />-->
|
||||
<!-- <el-button size="large" class="w-full" :disabled="!!countdownTime" @click="checkUser">-->
|
||||
<!-- <span v-if="!countdownTime">发送验证码</span>-->
|
||||
<!-- <span v-else>已发送 {{ countdownTime }} s</span>-->
|
||||
<!-- </el-button>-->
|
||||
<!-- </el-space>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item>-->
|
||||
<!-- <el-checkbox v-model="form.isAgree">我已阅读并同意</el-checkbox>-->
|
||||
<!-- <a href="#" class="text-gray-700">《用户协议》</a>-->
|
||||
<!-- <a href="#" class="text-gray-700">《隐私政策》</a>-->
|
||||
<!-- <a href="#" class="text-gray-700">《产品服务协议》</a>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item>-->
|
||||
<!-- <el-button type="primary" size="large" class="w-full" :disabled="!form.isAgree" @click="onRegister">注册</el-button>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-form>-->
|
||||
<!-- </div>-->
|
||||
<!-- </el-tab-pane>-->
|
||||
</el-tabs>
|
||||
</el-space>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {useConfigInfo, useToken, useUser, useWebsite} from "~/composables/configState";
|
||||
import useFormData from '@/utils/use-form-data';
|
||||
import type { User } from '@/api/system/user/model';
|
||||
import { ref } from 'vue'
|
||||
import type {FormInstance, FormRules, UploadProps, UploadUserFile} from 'element-plus'
|
||||
import { Shop, Key, Avatar, Briefcase } from '@element-plus/icons-vue'
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult} from "~/api";
|
||||
import type {CaptchaResult, LoginResult} from "~/api/passport/login/model";
|
||||
import type {ShopMerchantApply} from "~/api/shop/shopMerchantApply/model";
|
||||
import {useClientRequest} from "~/composables/useClientRequest";
|
||||
|
||||
// 配置信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const website = useWebsite();
|
||||
const config = useConfigInfo();
|
||||
const token = useToken();
|
||||
const user = useUser();
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
const activeName = ref('account')
|
||||
|
||||
// 登录注册切换
|
||||
const loginBar = ref<boolean>(true)
|
||||
// 验证码 base64 数据
|
||||
const captcha = ref('');
|
||||
// 验证码内容, 实际项目去掉
|
||||
const text = ref('');
|
||||
// 图形验证码
|
||||
const imgCode = ref('');
|
||||
// 发送验证码按钮loading
|
||||
const codeLoading = ref(false);
|
||||
// 验证码倒计时时间
|
||||
const countdownTime = ref(0);
|
||||
// 验证码倒计时定时器
|
||||
let countdownTimer: number | null = null;
|
||||
|
||||
if(getIdBySpm(0) == 'register'){
|
||||
loginBar.value = false;
|
||||
}
|
||||
|
||||
// 配置信息
|
||||
const { form,assignFields, resetFields } = useFormData<User>({
|
||||
userId: undefined,
|
||||
companyName: undefined,
|
||||
email: undefined,
|
||||
username: undefined,
|
||||
phone: undefined,
|
||||
password: undefined,
|
||||
code: undefined,
|
||||
smsCode: undefined,
|
||||
isAgree: false,
|
||||
remember: true,
|
||||
isSuperAdmin: true
|
||||
});
|
||||
|
||||
/* 显示发送短信验证码弹窗 */
|
||||
const openImgCodeModal = () => {
|
||||
if (!form.phone) {
|
||||
ElMessage.error('请输入手机号码');
|
||||
return;
|
||||
}
|
||||
// imgCode.value = text.value;
|
||||
};
|
||||
|
||||
const checkUser = async () => {
|
||||
const {data: hasUser } = await useServerRequest<ApiResult<CaptchaResult>>('/existence',{baseURL: runtimeConfig.public.apiServer,method: "get",params: {
|
||||
field: 'phone', value: form.phone
|
||||
}});
|
||||
if(hasUser.value?.code == 0 || loginBar.value){
|
||||
await sendCode();
|
||||
}
|
||||
if(hasUser.value?.code != 0 && loginBar.value){
|
||||
ElMessage.error('该手机号码未注册');
|
||||
}
|
||||
};
|
||||
|
||||
// 验证规则
|
||||
const rules = reactive<FormRules<User>>({
|
||||
phone: [
|
||||
{required: true, message: '请输入手机号码', trigger: 'blur'},
|
||||
{pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur'},
|
||||
],
|
||||
password: [
|
||||
{required: true, message: '请输入密码', trigger: 'blur'},
|
||||
],
|
||||
code: [
|
||||
{required: true, message: '请输入验证码', trigger: 'blur'},
|
||||
],
|
||||
smsCode: [
|
||||
{required: true, message: '请输入短信验证码', trigger: 'blur'},
|
||||
],
|
||||
username: [
|
||||
{required: true, message: '请输入用户名', trigger: 'blur'},
|
||||
],
|
||||
companyName: [
|
||||
{required: true, message: '请输入公司名称', trigger: 'blur'},
|
||||
],
|
||||
email: [
|
||||
{required: true, message: '请输入邮箱', trigger: 'blur'},
|
||||
{pattern: /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/, message: '请输入正确的邮箱', trigger: 'blur'},
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
/* 发送短信验证码 */
|
||||
const sendCode = async () => {
|
||||
if (!form.phone) {
|
||||
ElMessage.error('请输入手机号码');
|
||||
return;
|
||||
}
|
||||
imgCode.value = text.value;
|
||||
codeLoading.value = true;
|
||||
|
||||
const {data: smsCode } = await useServerRequest<ApiResult<CaptchaResult>>('/sendSmsCaptcha',{baseURL: runtimeConfig.public.apiServer,method: "post",body: {
|
||||
phone: form.phone
|
||||
}});
|
||||
if(smsCode.value?.code == 0){
|
||||
codeLoading.value = false;
|
||||
countdownTime.value = 30;
|
||||
// 开始对按钮进行倒计时
|
||||
countdownTimer = window.setInterval(() => {
|
||||
if (countdownTime.value <= 1) {
|
||||
countdownTimer && clearInterval(countdownTimer);
|
||||
countdownTimer = null;
|
||||
}
|
||||
countdownTime.value--;
|
||||
}, 1000);
|
||||
}
|
||||
if(smsCode.value?.code != 0){
|
||||
ElMessage.error(smsCode.value?.message);
|
||||
}
|
||||
};
|
||||
|
||||
const navigateTo = (url: string) => {
|
||||
window.location.href = url;
|
||||
}
|
||||
|
||||
const onLoginBar = () => {
|
||||
loginBar.value = !loginBar.value
|
||||
activeName.value = loginBar.value ? 'account' : 'sms'
|
||||
}
|
||||
|
||||
/* 获取图形验证码 */
|
||||
const changeCaptcha = async () => {
|
||||
const {data: captchaInfo } = await useServerRequest<ApiResult<CaptchaResult>>('/captcha',{baseURL: runtimeConfig.public.apiServer});
|
||||
const captchaData = captchaInfo.value?.data
|
||||
if(captchaData){
|
||||
captcha.value = captchaData.base64;
|
||||
text.value = captchaData.text;
|
||||
}
|
||||
|
||||
// 已经登录跳转首页
|
||||
if(token.value && token.value.length > 0){
|
||||
navigateTo('/user')
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
useHead({
|
||||
title: `登录页 - ${config.value?.siteName || 'WEB应用开发平台'}`,
|
||||
meta: [{ name: website.value.keywords, content: website.value.comments }]
|
||||
});
|
||||
|
||||
/**
|
||||
* 执行登录
|
||||
*/
|
||||
const onSubmit = async () => {
|
||||
if(!form.username){
|
||||
ElMessage.error('请填登录账号')
|
||||
return;
|
||||
}
|
||||
if(!form.password){
|
||||
ElMessage.error('请填密码')
|
||||
return;
|
||||
}
|
||||
if(!form.code){
|
||||
ElMessage.error('请填验证码')
|
||||
return;
|
||||
}
|
||||
const {data: response} = await useServerRequest<ApiResult<LoginResult>>('/login',{baseURL: runtimeConfig.public.apiServer,method: "post",body: form})
|
||||
// 登录成功
|
||||
if(response.value?.code == 0){
|
||||
ElMessage.success(response.value?.message)
|
||||
await doLogin(response.value.data)
|
||||
}
|
||||
if(response.value?.code != 0){
|
||||
ElMessage.error(response.value?.message)
|
||||
await changeCaptcha()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 短信验证码登录
|
||||
*/
|
||||
const onSubmitBySms = async () => {
|
||||
if(!form.phone){
|
||||
ElMessage.error('请填手机号码')
|
||||
return;
|
||||
}
|
||||
if(!form.code){
|
||||
ElMessage.error('请填验证码')
|
||||
return;
|
||||
}
|
||||
const {data: response} = await useServerRequest<ApiResult<LoginResult>>('/loginBySms',{baseURL: runtimeConfig.public.apiServer,method: "post",body: {
|
||||
phone: form.phone,
|
||||
code: form.code,
|
||||
isSuperAdmin: true
|
||||
}})
|
||||
// 登录成功
|
||||
if(response.value?.code == 0){
|
||||
ElMessage.success(response.value?.message)
|
||||
await doLogin(response.value.data)
|
||||
}
|
||||
if(response.value?.code != 0){
|
||||
ElMessage.error(response.value?.message)
|
||||
await changeCaptcha()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 账号密码注册
|
||||
*/
|
||||
const onRegister = async () => {
|
||||
if(!form.phone){
|
||||
ElMessage.error('请填手机号码')
|
||||
return;
|
||||
}
|
||||
if(!form.companyName){
|
||||
ElMessage.error('请填写企业名称')
|
||||
return;
|
||||
}
|
||||
if(!form.code){
|
||||
ElMessage.error('请填验证码')
|
||||
return;
|
||||
}
|
||||
if(!form.email){
|
||||
ElMessage.error('请填邮箱')
|
||||
return;
|
||||
}
|
||||
const loading = ElLoading.service({
|
||||
lock: true,
|
||||
text: 'Loading'
|
||||
})
|
||||
await useClientRequest<ApiResult<LoginResult>>('/register',{method: "post",body: {
|
||||
companyName: form.companyName,
|
||||
username: form.phone,
|
||||
phone: form.phone,
|
||||
password: form.password,
|
||||
code: form.code,
|
||||
email: form.email,
|
||||
isSuperAdmin: true
|
||||
}}).then(response => {
|
||||
// 登录成功
|
||||
if(response?.code == 0){
|
||||
loading.close();
|
||||
ElMessage.success(response?.message)
|
||||
doLogin(response.data)
|
||||
}
|
||||
if(response?.code != 0){
|
||||
loading.close;
|
||||
ElMessage.error(response?.message)
|
||||
changeCaptcha()
|
||||
}
|
||||
}).catch(() => {
|
||||
loading.close();
|
||||
}).finally(() => {
|
||||
loading.close();
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 登录成功执行
|
||||
const doLogin = async (data: any) => {
|
||||
const access_token = data?.access_token
|
||||
if(access_token){
|
||||
localStorage.setItem('token',access_token);
|
||||
token.value = access_token;
|
||||
console.log(access_token,'access_token')
|
||||
}
|
||||
if(data.user){
|
||||
user.value.userId = data.user.userId;
|
||||
user.value.phone = data.user.phone;
|
||||
user.value.gradeId = data.user.gradeId;
|
||||
user.value.gradeName = data.user.gradeName;
|
||||
user.value.avatar = data.user.avatar;
|
||||
user.value.balance = data.user.balance;
|
||||
localStorage.setItem('UserId',data.user.userId);
|
||||
localStorage.setItem('Avatar',data.user.avatar);
|
||||
localStorage.setItem('TID_ADMIN',data.user.tenantId);
|
||||
}
|
||||
setTimeout(() => {
|
||||
navigateTo('/')
|
||||
return;
|
||||
},500)
|
||||
}
|
||||
|
||||
changeCaptcha();
|
||||
</script>
|
||||
<style lang="less">
|
||||
.login{
|
||||
background: url("https://oss.wsdns.cn/20240904/6f5dc87c37334c4da3453826352a37d1.jpg");
|
||||
background-size: 100%;
|
||||
}
|
||||
|
||||
/* 改变未激活标签的颜色 */
|
||||
.el-tabs__item {
|
||||
color: #606266;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
/* 改变激活标签的颜色 */
|
||||
//.el-tabs__item.is-active {
|
||||
// color: #cf1313;
|
||||
//}
|
||||
//
|
||||
///* 改变标签边界的颜色 */
|
||||
//.el-tabs__item:not(.is-disabled):focus,
|
||||
//.el-tabs__item:not(.is-disabled):hover {
|
||||
// border-color: #cf1313;
|
||||
//}
|
||||
//
|
||||
///* 改变标签栏指示器的颜色 */
|
||||
//.el-tabs--top .el-tabs__active-bar,
|
||||
//.el-tabs--bottom .el-tabs__active-bar {
|
||||
// background: #cf1313;
|
||||
//}
|
||||
</style>
|
||||
49
pages/passport/register.vue
Normal file
49
pages/passport/register.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<PageBanner title="入驻" desc="Register Account"/>
|
||||
<div class="login-layout m-auto sm:w-screen-xl w-full">
|
||||
<div class="m-auto flex sm:flex-row flex-col sm:px-0 px-3 ">
|
||||
<div class="flash bg-white rounded-lg px-8 py-4 w-full">
|
||||
<Auth @done="reload"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {useConfigInfo, useWebsite} from "~/composables/configState";
|
||||
import {ref} from 'vue'
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult} from "~/api";
|
||||
import Auth from './components/Auth.vue';
|
||||
import type {ShopMerchantApply} from "~/api/shop/shopMerchantApply/model";
|
||||
import Base from "~/pages/user/components/Base.vue";
|
||||
|
||||
// 配置信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const route = useRoute();
|
||||
const activeIndex = ref('');
|
||||
const website = useWebsite()
|
||||
const config = useConfigInfo();
|
||||
const merchantApply = ref<ShopMerchantApply>();
|
||||
|
||||
const reload = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<ShopMerchantApply>>('/shop/shop-merchant-apply/getByUserId', {baseURL: runtimeConfig.public.apiServer})
|
||||
if (response.value?.data) {
|
||||
merchantApply.value = response.value.data;
|
||||
}
|
||||
if(config.value){
|
||||
useHead({
|
||||
title: `实名认证 - ${config.value?.siteName}`,
|
||||
meta: [{name: website.value.keywords, content: website.value.comments}]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
activeIndex.value = path;
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
67
pages/product/checkout/components/PageBanner.vue
Normal file
67
pages/product/checkout/components/PageBanner.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<div class="banner m-auto relative sm:flex">
|
||||
<svg viewBox="0 0 1440 181" fill="none" xmlns="http://www.w3.org/2000/svg"
|
||||
class="pointer-events-none absolute w-full top-[-2px] transition-all text-green-5 flex-shrink-0 opacity-100 duration-[400ms] opacity-80 -z-10">
|
||||
<mask id="path-1-inside-1_414_5526" fill="white">
|
||||
<path d="M0 0H1440V181H0V0Z"></path>
|
||||
</mask>
|
||||
<path d="M0 0H1440V181H0V0Z" fill="url(#paint0_linear_414_5526)" fill-opacity="0.22"></path>
|
||||
<path d="M0 2H1440V-2H0V2Z" fill="url(#paint1_linear_414_5526)" mask="url(#path-1-inside-1_414_5526)"></path>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_414_5526" x1="720" y1="0" x2="720" y2="181" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_414_5526" x1="0" y1="90.5" x2="1440" y2="90.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor" stop-opacity="0"></stop>
|
||||
<stop offset="0.395" stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<div class="md:w-screen-xl m-auto px-3">
|
||||
<Breadcrumb :data="form" />
|
||||
<div class="md:py-8 sm:py-16 md:px-0 px-4 py-8" _path="/templates" _dir="" _draft="false" _partial="false"
|
||||
_locale=""
|
||||
_id="content:4.templates.yml" _type="yaml" _source="content" _file="4.templates.yml" _stem="4.templates"
|
||||
_extension="yml">
|
||||
<el-steps
|
||||
style="max-width: 600px"
|
||||
:space="200"
|
||||
class="px-3"
|
||||
:active="active"
|
||||
finish-status="success"
|
||||
>
|
||||
<el-step title="选择套餐" />
|
||||
<el-step title="订单确认" />
|
||||
<el-step title="支付订单" />
|
||||
<el-step title="完成订购" />
|
||||
</el-steps>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import Breadcrumb from "~/components/Breadcrumb.vue";
|
||||
import type {Article} from "~/api/cms/article/model";
|
||||
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
title?: string;
|
||||
desc?: string;
|
||||
buyUrl?: string;
|
||||
form?: Article;
|
||||
active?: number;
|
||||
}>(),
|
||||
{
|
||||
title: 'Templates',
|
||||
desc: 'Explore community templates to get up and running in a few seconds.',
|
||||
demoUrl: '/product/website',
|
||||
buyUrl: 'https://github.com/websoft9/ansible-templates',
|
||||
}
|
||||
);
|
||||
|
||||
</script>
|
||||
226
pages/product/checkout/index.vue
Normal file
226
pages/product/checkout/index.vue
Normal file
@@ -0,0 +1,226 @@
|
||||
<template>
|
||||
<PageBanner :form="form" :active="active" />
|
||||
<div class="page-main md:w-screen-xl m-auto p-3">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="16" :xs="24">
|
||||
<el-card shadow="hover" class="mb-4">
|
||||
<template #header>
|
||||
<div class="card-header font-bold">
|
||||
<span>订单确认</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-descriptions :title="`订购产品`" :column="1" class="mb-4">
|
||||
<el-descriptions-item label="产品名称:">{{ form.title }}</el-descriptions-item>
|
||||
<el-descriptions-item label="产品类型:">{{ form.type == 1 ? '插件' : '完整应用' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="套餐版本:">标准版</el-descriptions-item>
|
||||
<el-descriptions-item label="购买数量:">1 {{ form.unitName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="购买时长:">1 年</el-descriptions-item>
|
||||
<el-descriptions-item label="到期时间:">2025-10-06</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-descriptions title="其他服务" :column="1" class="mb-4">
|
||||
<el-descriptions-item label="UI设计服务:">不包含</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-descriptions title="合计:" :column="1" class="mb-4">
|
||||
<el-descriptions-item label="总金额:">¥3000.00</el-descriptions-item>
|
||||
<el-descriptions-item label="优惠价格:">¥1280.00</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-card>
|
||||
<el-card shadow="hover" class="mb-4">
|
||||
<template #header>
|
||||
<div class="card-header font-bold">
|
||||
<span>服务协议</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="flex items-center">
|
||||
<el-checkbox v-model="isAgree" @click="changeIsAgree"></el-checkbox>
|
||||
<span class="ml-1">我已阅读并同意</span>
|
||||
<span class="text-blue-400 hover:text-blue-500 cursor-pointer" @click="openSpmUrl(`/detail`, {}, 357, true)">《用户协议》</span>
|
||||
<span class="text-blue-400 hover:text-blue-500 cursor-pointer" @click="openSpmUrl(`/detail`, {}, 69, true)">《隐私协议》</span>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="8" :xs="24">
|
||||
<el-card shadow="hover" class="mb-4">
|
||||
<template #header>
|
||||
<div class="card-header font-bold">
|
||||
<span>扫码支付</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="flex justify-center">
|
||||
<el-radio-group v-model="form.code">
|
||||
<el-radio-button value="1" border>微信支付</el-radio-button>
|
||||
<el-radio-button value="12" border>支付宝</el-radio-button>
|
||||
<el-radio-button value="24" border>余额支付</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div class="flex justify-center py-4">
|
||||
<el-avatar :size="250" src="https://oss.wsdns.cn/20240409/247a492abda94b08ace33fa5405628ca.jpeg"
|
||||
shape="square" />
|
||||
</div>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="danger" :disabled="!isAgree" class="w-full" size="large" @click="onSubmit">去支付</el-button>
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
{{ form }}
|
||||
<!-- <div class="login-layout mt-[100px] m-auto sm:w-screen-xl w-full">-->
|
||||
<!-- <div class="mt-[100px] m-auto flex sm:flex-row flex-col sm:p-0 p-3">-->
|
||||
<!-- <div class="flash bg-white rounded-lg px-7 py-4 w-full">-->
|
||||
<!-- <el-tabs class="flash bg-white ml-0">-->
|
||||
<!-- <el-tab-pane label="个人开发者">-->
|
||||
<!-- <el-form :model="form" label-width="auto" size="large" label-position="top" class="sm:w-screen-md w-full sm:py-2">-->
|
||||
<!-- <el-form-item label="商品类型">-->
|
||||
<!-- <el-input v-model="form.title" placeholder="请输入真实姓名" />-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item label="资源包类型">-->
|
||||
<!-- <el-input v-model="form.productId" placeholder="请输入证件号码" />-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item label="身份证(正面)">-->
|
||||
<!-- <Upload />-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item label="身份证(反面)">-->
|
||||
<!-- <Upload />-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item>-->
|
||||
<!-- <el-button type="primary" size="large" @click="onSubmit">提交</el-button>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-form>-->
|
||||
<!-- </el-tab-pane>-->
|
||||
<!-- </el-tabs>-->
|
||||
<!-- </div>-->
|
||||
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {useConfigInfo, useToken, useWebsite} from "~/composables/configState";
|
||||
import useFormData from '@/utils/use-form-data';
|
||||
import type {User} from '@/api/system/user/model';
|
||||
import {ref} from 'vue'
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult} from "~/api";
|
||||
import type {Product} from "~/api/oa/product/model";
|
||||
import PageBanner from "./components/PageBanner.vue";
|
||||
|
||||
// 配置信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const route = useRoute();
|
||||
const activeIndex = ref('');
|
||||
const website = useWebsite()
|
||||
const isAgree = ref<boolean>(false);
|
||||
const active = ref<number>(2);
|
||||
const config = useConfigInfo();
|
||||
const token = useToken();
|
||||
const userInfo = ref<User>();
|
||||
|
||||
// 配置信息
|
||||
const {form, assignFields} = useFormData<Product>({
|
||||
// 自增ID
|
||||
productId: undefined,
|
||||
// 类型 0软件产品 1实物商品 2虚拟商品
|
||||
type: undefined,
|
||||
// 产品编码
|
||||
code: undefined,
|
||||
// 产品标题
|
||||
title: undefined,
|
||||
// 封面图
|
||||
image: undefined,
|
||||
// 产品详情
|
||||
content: undefined,
|
||||
// 父级分类ID
|
||||
parentId: undefined,
|
||||
// 产品分类ID
|
||||
categoryId: undefined,
|
||||
// 产品规格 0单规格 1多规格
|
||||
specs: undefined,
|
||||
// 货架
|
||||
position: undefined,
|
||||
// 单位名称 (个)
|
||||
unitName: undefined,
|
||||
// 进货价格
|
||||
price: undefined,
|
||||
// 销售价格
|
||||
salePrice: undefined,
|
||||
// 库存计算方式(10下单减库存 20付款减库存)
|
||||
deductStockType: undefined,
|
||||
// 轮播图
|
||||
files: undefined,
|
||||
// 销量
|
||||
sales: undefined,
|
||||
// 库存
|
||||
stock: undefined,
|
||||
// 消费赚取积分
|
||||
gainIntegral: undefined,
|
||||
// 推荐
|
||||
recommend: undefined,
|
||||
// 商户ID
|
||||
merchantId: undefined,
|
||||
// 状态(0:未上架,1:上架)
|
||||
isShow: undefined,
|
||||
// 状态, 0上架 1待上架 2待审核 3审核不通过
|
||||
status: undefined,
|
||||
// 备注
|
||||
comments: undefined,
|
||||
// 排序号
|
||||
sortNumber: undefined,
|
||||
// 用户ID
|
||||
userId: undefined,
|
||||
// 是否删除, 0否, 1是
|
||||
deleted: undefined,
|
||||
// 租户id
|
||||
tenantId: undefined,
|
||||
// 创建时间
|
||||
createTime: undefined,
|
||||
// 修改时间
|
||||
updateTime: undefined,
|
||||
});
|
||||
|
||||
const changeIsAgree = (index: number) => {
|
||||
if(index){
|
||||
active.value = 3;
|
||||
}
|
||||
}
|
||||
|
||||
const onSubmit = async () => {
|
||||
const {data: modify} = await useServerRequest<ApiResult<User>>('/auth/user', {
|
||||
baseURL: runtimeConfig.public.apiServer,
|
||||
method: 'put',
|
||||
body: form
|
||||
})
|
||||
if (modify.value?.code == 0) {
|
||||
ElMessage.success('修改成功')
|
||||
}
|
||||
}
|
||||
|
||||
const reload = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<Product>>(`/cms/cms-product/${getIdBySpm(5)}`, {baseURL: runtimeConfig.public.apiServer})
|
||||
if (response.value?.data) {
|
||||
assignFields(response.value?.data);
|
||||
form.categoryName = '订单确认'
|
||||
useHead({
|
||||
title: `${form.title}`,
|
||||
meta: [{name: website.value.keywords, content: website.value.comments}]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
activeIndex.value = path;
|
||||
console.log(path, '=>Path')
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.text-a123 {
|
||||
color: #f0f2f5;
|
||||
}
|
||||
</style>
|
||||
81
pages/product/components/CardList.vue
Normal file
81
pages/product/components/CardList.vue
Normal file
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in data?.list" :key="index">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer"
|
||||
@click="openSpmUrl(`/item`, item,item.companyId,true)">
|
||||
<el-image
|
||||
:src="item.image"
|
||||
:fit="fit" :lazy="true" class="w-full md:h-[150px] h-[199px] cursor-pointer bg-gray-50"/>
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col gap-1.5">
|
||||
<div class="flex-1 text-xl cursor-pointer flex items-center">
|
||||
{{ item.shortName }}
|
||||
<el-tag v-if="item.chargingMethod === 0" size="small" type="success" class="text-white ml-2"
|
||||
effect="dark">免费
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500 line-clamp-2">{{ item.comments }}</div>
|
||||
</div>
|
||||
<div class="button-group flex justify-between items-center mt-3">
|
||||
<el-space class="flex items-end">
|
||||
<el-avatar size="small" :src="item.companyLogo"/>
|
||||
<span class="text-gray-400 line-clamp-1 pr-2">{{ item.companyName }}</span>
|
||||
</el-space>
|
||||
<template v-if="item.isBuy">
|
||||
<el-button v-if="item.installed" type="primary" @click.stop="loginDeveloperCenterByToken(item)">控制台</el-button>
|
||||
<el-button v-else type="primary" @click.stop="loginDeveloperCenterByToken(item)">控制台</el-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-button v-if="item.chargingMethod == 0" type="primary" @click.stop="loginDeveloperCenterByToken(item)">控制台</el-button>
|
||||
<el-button v-else type="warning" @click.stop="openSpmUrl(`/product/create`,item,item.companyId,true)">立即开通
|
||||
</el-button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div class="flex justify-center py-3">
|
||||
<el-pagination background layout="prev, pager, next" :total="data?.count" @change="onPage"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {loginAdminByToken, loginByToken, openSpmUrl} from "~/utils/common";
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
import type {PageResult} from "~/api";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
data?: PageResult<Company>;
|
||||
disabled?: boolean;
|
||||
fit?: any;
|
||||
}>(),
|
||||
{
|
||||
fit: 'cover'
|
||||
}
|
||||
);
|
||||
|
||||
const tid = ref<number>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done', index: number): void;
|
||||
}>();
|
||||
|
||||
// const load = () => {
|
||||
// if(!props.disabled){
|
||||
// emit('done')
|
||||
// }
|
||||
// }
|
||||
|
||||
const onPage = (index: number) => {
|
||||
emit('done', index)
|
||||
}
|
||||
tid.value = localStorage.getItem('TenantId');
|
||||
</script>
|
||||
67
pages/product/create/components/PageBanner.vue
Normal file
67
pages/product/create/components/PageBanner.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<div class="banner m-auto relative sm:flex">
|
||||
<svg viewBox="0 0 1440 181" fill="none" xmlns="http://www.w3.org/2000/svg"
|
||||
class="pointer-events-none absolute w-full top-[-2px] transition-all text-green-5 flex-shrink-0 opacity-100 duration-[400ms] opacity-80 -z-10">
|
||||
<mask id="path-1-inside-1_414_5526" fill="white">
|
||||
<path d="M0 0H1440V181H0V0Z"></path>
|
||||
</mask>
|
||||
<path d="M0 0H1440V181H0V0Z" fill="url(#paint0_linear_414_5526)" fill-opacity="0.22"></path>
|
||||
<path d="M0 2H1440V-2H0V2Z" fill="url(#paint1_linear_414_5526)" mask="url(#path-1-inside-1_414_5526)"></path>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_414_5526" x1="720" y1="0" x2="720" y2="181" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_414_5526" x1="0" y1="90.5" x2="1440" y2="90.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor" stop-opacity="0"></stop>
|
||||
<stop offset="0.395" stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<div class="md:w-screen-xl m-auto px-3">
|
||||
<Breadcrumb :data="form" />
|
||||
<div class="md:py-8 sm:py-16 md:px-0 px-4 py-8" _path="/templates" _dir="" _draft="false" _partial="false"
|
||||
_locale=""
|
||||
_id="content:4.templates.yml" _type="yaml" _source="content" _file="4.templates.yml" _stem="4.templates"
|
||||
_extension="yml">
|
||||
<el-steps
|
||||
style="max-width: 600px"
|
||||
:space="200"
|
||||
class="px-3"
|
||||
:active="active"
|
||||
finish-status="success"
|
||||
>
|
||||
<el-step title="选择套餐" />
|
||||
<el-step title="订单确认" />
|
||||
<el-step title="支付订单" />
|
||||
<el-step title="完成订购" />
|
||||
</el-steps>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import Breadcrumb from "~/components/Breadcrumb.vue";
|
||||
import type {Article} from "~/api/cms/article/model";
|
||||
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
title?: string;
|
||||
desc?: string;
|
||||
buyUrl?: string;
|
||||
form?: Article;
|
||||
active?: number;
|
||||
}>(),
|
||||
{
|
||||
title: 'Templates',
|
||||
desc: 'Explore community templates to get up and running in a few seconds.',
|
||||
demoUrl: '/product/website',
|
||||
buyUrl: 'https://github.com/websoft9/ansible-templates'
|
||||
}
|
||||
);
|
||||
|
||||
</script>
|
||||
439
pages/product/create/index.vue
Normal file
439
pages/product/create/index.vue
Normal file
@@ -0,0 +1,439 @@
|
||||
<template>
|
||||
<PageBanner :form="form" :active="active"/>
|
||||
<div class="page-main md:w-screen-xl m-auto p-3">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="16" :xs="24">
|
||||
<el-form :model="form" label-width="auto" label-position="left" class="w-full">
|
||||
<el-card shadow="hover" class="mb-4">
|
||||
<template #header>
|
||||
<div class="card-header font-bold">
|
||||
<span v-if="form.type === 0">【产品】</span>
|
||||
<span v-if="form.type === 1">【插件】</span>
|
||||
<span>{{ form.shortName }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="flex flex-col">
|
||||
<el-form-item label="套餐版本">
|
||||
<el-radio-group v-model="form.image">
|
||||
<el-radio-button value="1" border>基础版</el-radio-button>
|
||||
<el-radio-button value="2" border disabled>专业版</el-radio-button>
|
||||
<el-radio-button value="3" border disabled>定制版</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称">
|
||||
<el-input v-model="cart.appName" style="width: 360px" placeholder="网宿软件"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="二级域名">
|
||||
<el-input
|
||||
v-model="cart.domain"
|
||||
style="width: 360px"
|
||||
placeholder="websoft"
|
||||
>
|
||||
<template #prepend>Https://</template>
|
||||
<template #append>.wsdns.cn</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="类型">-->
|
||||
<!-- <el-radio-button v-if="form.type == 0" border>完整应用</el-radio-button>-->
|
||||
<!-- <el-radio-button v-if="form.type == 1" border>插件</el-radio-button>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item label="交付方式" v-if="form.deliveryMethod">-->
|
||||
<!-- <el-radio-button v-if="form.deliveryMethod == 1" border>SaaS交付</el-radio-button>-->
|
||||
<!-- <el-radio-button v-if="form.deliveryMethod == 2" border>源码交付</el-radio-button>-->
|
||||
<!-- </el-form-item>-->
|
||||
<el-form-item label="购买时长" v-if="form.chargingMethod && form.chargingMethod > 1">
|
||||
<el-radio-group v-model="cart.month" @change="handleChargingMethod">
|
||||
<el-radio-button :value="1" border>1个月</el-radio-button>
|
||||
<el-radio-button :value="12" border>1年</el-radio-button>
|
||||
<el-radio-button :value="24" border>2年</el-radio-button>
|
||||
<el-radio-button :value="36" border>3年</el-radio-button>
|
||||
<el-radio-button :value="60" border>5年</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 单价 {{ form.price }} x 购买时长 {{ cart.month }} x 数量 {{ cart.num }} x 折扣 0.1= {{ cart.totalPrice }}-->
|
||||
<!-- <el-form-item label="购买数量">-->
|
||||
<!-- <el-input-number v-model="cart.num" :min="1" :max="form.canBuyNumber" @change="handleChange"/>-->
|
||||
<!-- </el-form-item>-->
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card shadow="hover" class="mb-4">
|
||||
<template #header>
|
||||
<div class="card-header font-bold">
|
||||
<span>服务协议</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="flex items-center">
|
||||
<el-checkbox v-model="isAgree" @change="changeIsAgree"></el-checkbox>
|
||||
<span class="ml-1">我已阅读并同意</span>
|
||||
<span class="text-blue-400 hover:text-blue-500 cursor-pointer"
|
||||
@click="openSpmUrl(`/detail`, {}, 357, true)">《用户协议》</span>
|
||||
<span class="text-blue-400 hover:text-blue-500 cursor-pointer"
|
||||
@click="openSpmUrl(`/detail`, {}, 69, true)">《隐私协议》</span>
|
||||
<span class="text-blue-400 hover:text-blue-500 cursor-pointer"
|
||||
@click="openSpmUrl(`/detail`, {}, 69, true)">《产品购买协议》</span>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-form>
|
||||
</el-col>
|
||||
<el-col :span="8" :xs="24">
|
||||
<el-card shadow="hover" class="mb-4">
|
||||
<template #header>
|
||||
<div class="card-header font-bold">
|
||||
<span>订单详情</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-descriptions :title="`订购产品`" :column="1" class="mb-4">
|
||||
<el-descriptions-item label="产品名称:">{{ form.shortName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="产品类型:">{{ form.type == 1 ? '插件' : '完整应用' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="交付方式:">{{ form.deliveryMethod == 1 ? 'SaaS交付' : '源码交付' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="应用名称:">{{ cart.appName }}</el-descriptions-item>
|
||||
<!-- <el-descriptions-item label="套餐版本:">标准版</el-descriptions-item>-->
|
||||
<el-descriptions-item label="购买数量:">{{ cart.num }} 套</el-descriptions-item>
|
||||
<el-descriptions-item label="购买时长:">{{ cart.month }} 个月</el-descriptions-item>
|
||||
<!-- <el-descriptions-item label="到期时间:">2025-10-06</el-descriptions-item>-->
|
||||
</el-descriptions>
|
||||
<el-descriptions title="合计:" :column="1" class="mb-4">
|
||||
<el-descriptions-item label="订单金额:"><span class="line-through">¥</span><span
|
||||
class="text-xl line-through">{{ cart.totalPrice }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="实付金额:"><span class="text-red-600">¥</span><span
|
||||
class="font-bold text-xl text-red-600">{{ cart.payPrice }}</span></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-form-item>
|
||||
<el-button type="danger" class="w-full" :disabled="!isAgree" size="large" @click="onPay">去支付</el-button>
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
title="订单确认"
|
||||
align-center
|
||||
width="450"
|
||||
:before-close="() => visible = false"
|
||||
>
|
||||
<div class="flex justify-center pt-3">
|
||||
<el-radio-group v-model="cart.payType" @change="handlePayType">
|
||||
<el-radio-button :value="102" border>微信支付</el-radio-button>
|
||||
<!-- <el-radio-button :value="3" border>支付宝</el-radio-button>-->
|
||||
<el-radio-button :value="0" border>余额支付</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div class="flex justify-center py-4" v-if="cart.payType == 102">
|
||||
<el-avatar :size="250" :src="wxPayQrCode"
|
||||
shape="square"/>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer text-center pb-3" v-if="cart.payType != 0">
|
||||
<el-tag type="success" size="large">
|
||||
使用微信扫码完成支付
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="flex flex-col justify-center w-1/2 m-auto pb-12" v-if="cart.payType === 0">
|
||||
<span class="py-2 text-center">可用余额:¥{{ userInfo?.balance || 0.00 }}</span>
|
||||
<div class="flex flex-col">
|
||||
<el-input type="password" class="py-2" size="large" v-model="payPassword" maxlength="6" show-password placeholder="请输入支付密码" />
|
||||
<el-button type="danger" class="py-2 my-3" size="large" @click="onDone">
|
||||
确定支付
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {useConfigInfo, useToken, useUser, useWebsite} from "~/composables/configState";
|
||||
import useFormData from '@/utils/use-form-data';
|
||||
import type {User} from '@/api/system/user/model';
|
||||
import {ref} from 'vue'
|
||||
import QRCode from 'qrcode';
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult} from "~/api";
|
||||
import PageBanner from './components/PageBanner.vue';
|
||||
import type {FormInstance} from "element-plus";
|
||||
import {useClientRequest} from "~/composables/useClientRequest";
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
|
||||
// 配置信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const route = useRoute();
|
||||
const activeIndex = ref('');
|
||||
const website = useWebsite()
|
||||
const isAgree = ref<boolean>(true);
|
||||
const active = ref<number>(1);
|
||||
const visible = ref<boolean>(false);
|
||||
const wxPayQrCode = ref<string>()
|
||||
const payPassword = ref<string>()
|
||||
const formRef = ref<FormInstance>()
|
||||
const config = useConfigInfo();
|
||||
const token = useToken();
|
||||
const userInfo = useUser();
|
||||
const cart = useCart();
|
||||
const qrOptions = ref({
|
||||
width: 200, // 二维码宽度
|
||||
margin: 10, // 二维码边距
|
||||
color: { dark: '#000', light: '#fff' }, // 二维码颜色
|
||||
});
|
||||
|
||||
// 配置信息
|
||||
const {form, assignFields} = useFormData<Company>({
|
||||
companyId: undefined,
|
||||
menuId: undefined,
|
||||
type: undefined,
|
||||
appName: undefined,
|
||||
shortName: undefined,
|
||||
companyName: undefined,
|
||||
companyType: undefined,
|
||||
companyTypeMultiple: undefined,
|
||||
appType: undefined,
|
||||
companyLogo: undefined,
|
||||
image: undefined,
|
||||
companyCode: undefined,
|
||||
domain: undefined,
|
||||
phone: undefined,
|
||||
tel: undefined,
|
||||
email: undefined,
|
||||
InvoiceHeader: undefined,
|
||||
startTime: undefined,
|
||||
expirationTime: undefined,
|
||||
version: undefined,
|
||||
versionName: undefined,
|
||||
versionCode: undefined,
|
||||
members: undefined,
|
||||
storage: undefined,
|
||||
storageMax: undefined,
|
||||
buys: undefined,
|
||||
clicks: undefined,
|
||||
users: undefined,
|
||||
departments: undefined,
|
||||
industryParent: undefined,
|
||||
industryChild: undefined,
|
||||
country: undefined,
|
||||
province: undefined,
|
||||
city: undefined,
|
||||
region: undefined,
|
||||
address: undefined,
|
||||
latitude: undefined,
|
||||
longitude: undefined,
|
||||
businessEntity: undefined,
|
||||
comments: undefined,
|
||||
authentication: undefined,
|
||||
industryId: undefined,
|
||||
industryName: undefined,
|
||||
status: undefined,
|
||||
userId: undefined,
|
||||
official: undefined,
|
||||
deliveryMethod: undefined,
|
||||
chargingMethod: undefined,
|
||||
price: undefined,
|
||||
planId: undefined,
|
||||
sortNumber: undefined,
|
||||
authoritative: undefined,
|
||||
merchantId: undefined,
|
||||
tenantId: undefined,
|
||||
tenantName: undefined,
|
||||
tenantCode: undefined,
|
||||
modules: undefined,
|
||||
requestUrl: undefined,
|
||||
socketUrl: undefined,
|
||||
serverUrl: undefined,
|
||||
modulesUrl: undefined,
|
||||
merchantUrl: undefined,
|
||||
websiteUrl: undefined,
|
||||
mpWeixinCode: undefined,
|
||||
mpAlipayCode: undefined,
|
||||
h5Code: undefined,
|
||||
androidUrl: undefined,
|
||||
iosUrl: undefined,
|
||||
avatar: undefined,
|
||||
nickname: undefined,
|
||||
code: undefined,
|
||||
createTime: undefined,
|
||||
updateTime: undefined,
|
||||
password: undefined,
|
||||
password2: undefined,
|
||||
collection: undefined,
|
||||
recommend: undefined,
|
||||
title: undefined,
|
||||
parentName: undefined,
|
||||
categoryName: undefined,
|
||||
});
|
||||
|
||||
const handleChange = (index:any): void => {
|
||||
cart.value.num = index;
|
||||
computeTotalPrice();
|
||||
}
|
||||
|
||||
const handleChargingMethod = (index: any): void => {
|
||||
cart.value.month = index;
|
||||
computeTotalPrice();
|
||||
}
|
||||
|
||||
const computeTotalPrice = () => {
|
||||
// 计算公式 = (商品价格 * 数量 * 月份) / 折扣率
|
||||
cart.value.totalPrice = Math.round(Number(form.price)*Number(cart.value.num)*Number(cart.value.month));
|
||||
cart.value.payPrice = Math.round(Number(cart.value.totalPrice) * Number(0.1));
|
||||
}
|
||||
|
||||
const changeIsAgree = (index: any) => {
|
||||
if (index) {
|
||||
active.value = 2;
|
||||
}
|
||||
}
|
||||
|
||||
const handlePayType = (index: any) => {
|
||||
cart.value.payType = index;
|
||||
onPay();
|
||||
}
|
||||
|
||||
// 统一下单
|
||||
const onPay = () => {
|
||||
visible.value = true;
|
||||
active.value = 3;
|
||||
// 余额支付
|
||||
if(cart.value.payType == 0){
|
||||
return;
|
||||
}
|
||||
// 微信支付
|
||||
if(cart.value.payType == 102){
|
||||
useClientRequest<ApiResult<any>>(`/system/wx-native-pay/codeUrl`, {
|
||||
method: 'POST',
|
||||
body: cart.value
|
||||
}).then(async res => {
|
||||
if (res.code == 0) {
|
||||
try {
|
||||
// 生成二维码图像数据URL
|
||||
wxPayQrCode.value = await QRCode.toDataURL(res.data);
|
||||
} catch (error) {
|
||||
console.error('Error generating QR code:', error);
|
||||
}
|
||||
} else {
|
||||
return ElMessage.error(res.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
// 支付宝支付
|
||||
if(cart.value.payType == 3){
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const onSubmit = async () => {
|
||||
const {data: modify} = await useServerRequest<ApiResult<User>>('/auth/user', {
|
||||
baseURL: runtimeConfig.public.apiServer,
|
||||
method: 'put',
|
||||
body: form
|
||||
})
|
||||
if (modify.value?.code == 0) {
|
||||
ElMessage.success('修改成功')
|
||||
}
|
||||
}
|
||||
|
||||
const reload = async () => {
|
||||
// 要求登录
|
||||
if (!token.value || token.value == '') {
|
||||
navigateTo(`/passport/login`)
|
||||
return false;
|
||||
}
|
||||
// useClientRequest(`/auth/user`, {}).then(res => {
|
||||
// if(res?.data?.balance){
|
||||
// cart.value.balance = res?.data?.balance;
|
||||
// }
|
||||
// })
|
||||
const {data: response} = await useServerRequest<ApiResult<Company>>(`/system/company/${getIdBySpm(5)}`, {baseURL: runtimeConfig.public.apiServer})
|
||||
if (response.value?.data) {
|
||||
assignFields(response.value?.data);
|
||||
form.categoryName = '选择套餐';
|
||||
cart.value.comments = `购买${response.value?.data.shortName}`;
|
||||
if(response.value?.data.price){
|
||||
cart.value.payPrice = response.value?.data.price;
|
||||
cart.value.adminUrl = response.value?.data.domain;
|
||||
cart.value.menuId = response.value?.data.menuId;
|
||||
computeTotalPrice();
|
||||
}
|
||||
useHead({
|
||||
title: `${form.shortName}`,
|
||||
meta: [{name: website.value.keywords, content: website.value.comments}]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 扫码支付
|
||||
const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
ElMessage.error('还没有评分哦!')
|
||||
// if (form.rate === 0) {
|
||||
// ElMessage.error('还没有评分哦!')
|
||||
// return false;
|
||||
// }
|
||||
// form.productId = props.productId;
|
||||
// useClientRequest<ApiResult<any>>(`/cms/cms-product-comment/`, {
|
||||
// method: 'POST',
|
||||
// body: form
|
||||
// }).then(res => {
|
||||
// if (res.code == 0) {
|
||||
// ElMessage.success(res.message)
|
||||
// visible.value = false
|
||||
// resetFields();
|
||||
// emit('done',1)
|
||||
// } else {
|
||||
// return ElMessage.error(res.message)
|
||||
// }
|
||||
// })
|
||||
}
|
||||
|
||||
// 余额支付
|
||||
const onDone = () => {
|
||||
cart.value.type = 1;
|
||||
cart.value.list = [];
|
||||
cart.value.list?.push(form);
|
||||
useClientRequest<ApiResult<any>>(`/system/order/createOrder`, {
|
||||
method: 'POST',
|
||||
body: cart.value
|
||||
}).then(res => {
|
||||
if(res.code == 0){
|
||||
ElMessage.success('购买成功');
|
||||
visible.value = !visible.value;
|
||||
setTimeout(() => {
|
||||
openSpmUrl(`https://console.websoft.top/cost-center/order`)
|
||||
},500)
|
||||
}
|
||||
if(res.code == 1){
|
||||
ElMessage.error(res.message);
|
||||
}
|
||||
|
||||
}).catch(res => {
|
||||
|
||||
})
|
||||
|
||||
// useClientRequest<ApiResult<any>>(`/system/menu/install`, {
|
||||
// method: 'POST',
|
||||
// body: {
|
||||
// companyId: getIdBySpm(5)
|
||||
// }
|
||||
// }).then(res => {
|
||||
// if (res.code == 0) {
|
||||
// ElMessage.success(res.message)
|
||||
// } else {
|
||||
// return ElMessage.error(res.message)
|
||||
// }
|
||||
// })
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
activeIndex.value = path;
|
||||
console.log(path, '=>Path')
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.text-a123 {
|
||||
color: #f0f2f5;
|
||||
}
|
||||
</style>
|
||||
389
pages/product/create/product.vue
Normal file
389
pages/product/create/product.vue
Normal file
@@ -0,0 +1,389 @@
|
||||
<template>
|
||||
<PageBanner :form="form" :active="active"/>
|
||||
<div class="page-main md:w-screen-xl m-auto p-3">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="16" :xs="24">
|
||||
<el-form :model="form" label-width="auto" label-position="left" class="w-full">
|
||||
<el-card shadow="hover" class="mb-4">
|
||||
<template #header>
|
||||
<div class="card-header font-bold">
|
||||
<span v-if="form.type === 0">【产品】</span>
|
||||
<span v-if="form.type === 1">【插件】</span>
|
||||
<span>{{ form.title }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="flex flex-col">
|
||||
<el-form-item label="类型">
|
||||
<el-radio-button v-if="form.type == 0" border>完整应用</el-radio-button>
|
||||
<el-radio-button v-if="form.type == 1" border>插件</el-radio-button>
|
||||
</el-form-item>
|
||||
<el-form-item label="套餐版本">
|
||||
<el-radio-group v-model="form.image">
|
||||
<el-radio-button value="1" border>基础版</el-radio-button>
|
||||
<el-radio-button value="2" border>标准版</el-radio-button>
|
||||
<el-radio-button value="3" border>专业版</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="交付方式" v-if="form.deliveryMethod">-->
|
||||
<!-- <el-tag v-if="form.deliveryMethod == 1">SaaS交付</el-tag>-->
|
||||
<!-- <el-tag v-if="form.deliveryMethod == 2">源码交付</el-tag>-->
|
||||
<!-- </el-form-item>-->
|
||||
<el-form-item label="购买时长" v-if="form.chargingMethod && form.chargingMethod > 1">
|
||||
<el-radio-group v-model="form.chargingMethod" @change="handleChargingMethod">
|
||||
<el-radio-button :value="1" border>1个月</el-radio-button>
|
||||
<el-radio-button :value="12" border>1年</el-radio-button>
|
||||
<el-radio-button :value="24" border>2年</el-radio-button>
|
||||
<el-radio-button :value="36" border>3年</el-radio-button>
|
||||
<el-radio-button :value="60" border>5年</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="购买数量">-->
|
||||
<!-- <el-input-number v-model="cart.num" :min="1" :max="form.canBuyNumber" @change="handleChange"/>-->
|
||||
<!-- </el-form-item>-->
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card shadow="hover" class="mb-4">
|
||||
<template #header>
|
||||
<div class="card-header font-bold">
|
||||
<span>服务协议</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="flex items-center">
|
||||
<el-checkbox v-model="isAgree" @change="changeIsAgree"></el-checkbox>
|
||||
<span class="ml-1">我已阅读并同意</span>
|
||||
<span class="text-blue-400 hover:text-blue-500 cursor-pointer"
|
||||
@click="openSpmUrl(`/detail`, {}, 357, true)">《用户协议》</span>
|
||||
<span class="text-blue-400 hover:text-blue-500 cursor-pointer"
|
||||
@click="openSpmUrl(`/detail`, {}, 69, true)">《隐私协议》</span>
|
||||
<span class="text-blue-400 hover:text-blue-500 cursor-pointer"
|
||||
@click="openSpmUrl(`/detail`, {}, 69, true)">《产品购买协议》</span>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-form>
|
||||
</el-col>
|
||||
<el-col :span="8" :xs="24">
|
||||
<el-card shadow="hover" class="mb-4">
|
||||
<template #header>
|
||||
<div class="card-header font-bold">
|
||||
<span>配置清单</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-descriptions :title="`订购产品`" :column="1" class="mb-4">
|
||||
<el-descriptions-item label="产品名称:">{{ form.title }}</el-descriptions-item>
|
||||
<el-descriptions-item label="产品类型:">{{ form.type == 1 ? '插件' : '完整应用' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="套餐版本:">标准版</el-descriptions-item>
|
||||
<el-descriptions-item label="购买数量:">{{ cart.num }} 套</el-descriptions-item>
|
||||
<el-descriptions-item label="购买时长:">{{ cart.month }} 个月</el-descriptions-item>
|
||||
<el-descriptions-item label="到期时间:">2025-10-06</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-descriptions title="合计:" :column="1" class="mb-4">
|
||||
<el-descriptions-item label="订单金额:"><span class="line-through">¥</span><span
|
||||
class="text-xl line-through">{{ cart.totalPrice }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="实付金额:"><span class="text-red-600">¥</span><span
|
||||
class="font-bold text-2xl text-red-600">{{ cart.totalPrice }}</span></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-form-item>
|
||||
<el-button type="danger" class="w-full" :disabled="!isAgree" size="large" @click="onPay">去支付</el-button>
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
title="订单确认"
|
||||
align-center
|
||||
width="400"
|
||||
:before-close="() => visible = false"
|
||||
>
|
||||
<div class="flex justify-center pt-3">
|
||||
<el-radio-group v-model="cart.payType" @change="handlePayType">
|
||||
<el-radio-button :value="102" border>微信支付</el-radio-button>
|
||||
<el-radio-button :value="3" border>支付宝</el-radio-button>
|
||||
<el-radio-button :value="0" border>余额支付</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div class="flex justify-center py-4">
|
||||
<el-avatar :size="250" src="https://oss.wsdns.cn/20240409/247a492abda94b08ace33fa5405628ca.jpeg"
|
||||
shape="square"/>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer text-center pb-3">
|
||||
<el-button type="success" @click="onDone">
|
||||
已完成支付
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {useConfigInfo, useToken, useWebsite} from "~/composables/configState";
|
||||
import useFormData from '@/utils/use-form-data';
|
||||
import type {User} from '@/api/system/user/model';
|
||||
import {ref} from 'vue'
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult} from "~/api";
|
||||
import PageBanner from './components/PageBanner.vue';
|
||||
import type {FormInstance} from "element-plus";
|
||||
import {useClientRequest} from "~/composables/useClientRequest";
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
import type {CmsProduct} from "~/api/cms/cmsProduct/model";
|
||||
import type {CmsProductParameter} from "~/api/cms/cmsProductParameter/model";
|
||||
import type {CmsProductUrl} from "~/api/cms/cmsProductUrl/model";
|
||||
|
||||
// 配置信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const route = useRoute();
|
||||
const activeIndex = ref('');
|
||||
const website = useWebsite()
|
||||
const isAgree = ref<boolean>(true);
|
||||
const active = ref<number>(1);
|
||||
const visible = ref<boolean>(false);
|
||||
const formRef = ref<FormInstance>()
|
||||
const config = useConfigInfo();
|
||||
const token = useToken();
|
||||
const userInfo = ref<User>();
|
||||
const cart = useCart();
|
||||
|
||||
// 配置信息
|
||||
const {form, assignFields} = useFormData<CmsProduct>({
|
||||
// 自增ID
|
||||
productId: undefined,
|
||||
// 类型 0软件产品 1实物商品 2虚拟商品
|
||||
type: undefined,
|
||||
// 产品编码
|
||||
code: undefined,
|
||||
// 产品标题
|
||||
title: undefined,
|
||||
// 封面图
|
||||
image: undefined,
|
||||
// 产品详情
|
||||
content: undefined,
|
||||
// 父级分类ID
|
||||
parentId: undefined,
|
||||
// 产品分类ID
|
||||
categoryId: undefined,
|
||||
// 产品规格 0单规格 1多规格
|
||||
specs: undefined,
|
||||
// 货架
|
||||
position: undefined,
|
||||
// 单位名称 (个)
|
||||
unitName: undefined,
|
||||
// 进货价格
|
||||
price: undefined,
|
||||
// 销售价格
|
||||
salePrice: undefined,
|
||||
// 库存计算方式(10下单减库存 20付款减库存)
|
||||
deductStockType: undefined,
|
||||
// 轮播图
|
||||
files: undefined,
|
||||
// 销量
|
||||
sales: undefined,
|
||||
// 库存
|
||||
stock: undefined,
|
||||
// 安装次数
|
||||
install: undefined,
|
||||
// 消费赚取积分
|
||||
gainIntegral: undefined,
|
||||
// 计费方式
|
||||
durationMethod: undefined,
|
||||
// 推荐
|
||||
recommend: undefined,
|
||||
// 商户ID
|
||||
merchantId: undefined,
|
||||
merchantName: undefined,
|
||||
merchantAvatar: undefined,
|
||||
merchantComments: undefined,
|
||||
// 状态(0:未上架,1:上架)
|
||||
isShow: undefined,
|
||||
// 状态, 0上架 1待上架 2待审核 3审核不通过
|
||||
status: undefined,
|
||||
// 备注
|
||||
comments: undefined,
|
||||
// 排序号
|
||||
sortNumber: undefined,
|
||||
// 用户ID
|
||||
userId: undefined,
|
||||
// 是否删除, 0否, 1是
|
||||
deleted: undefined,
|
||||
// 租户id
|
||||
tenantId: undefined,
|
||||
// 创建时间
|
||||
createTime: undefined,
|
||||
// 修改时间
|
||||
updateTime: undefined,
|
||||
// 父级分类名称
|
||||
parentName: undefined,
|
||||
// 父级分类路径
|
||||
parentPath: undefined,
|
||||
// 分类名称
|
||||
categoryName: undefined,
|
||||
// 评分
|
||||
rate: undefined,
|
||||
// 是否已购买
|
||||
isBuy: undefined,
|
||||
// 是否已安装插件
|
||||
installed: undefined,
|
||||
// 产品参数
|
||||
parameters: undefined,
|
||||
// 产品链接
|
||||
links: undefined,
|
||||
// 插件入口
|
||||
path: undefined,
|
||||
// 标签
|
||||
tag: undefined,
|
||||
// 菜单ID
|
||||
menuId: undefined,
|
||||
});
|
||||
|
||||
const handleChange = (index:any): void => {
|
||||
cart.value.num = index;
|
||||
computeTotalPrice();
|
||||
}
|
||||
|
||||
const handleChargingMethod = (index: any): void => {
|
||||
cart.value.month = index;
|
||||
computeTotalPrice();
|
||||
}
|
||||
|
||||
const computeTotalPrice = () => {
|
||||
// cart.value.totalPrice = cart.value.num * cart.value.payPrice * cart.value.month;
|
||||
// cart.value.totalPrice = Math.round(cart.value.totalPrice * 100) / 100;
|
||||
}
|
||||
|
||||
const changeIsAgree = (index: any) => {
|
||||
if (index) {
|
||||
active.value = 2;
|
||||
}
|
||||
}
|
||||
|
||||
const handlePayType = (index: any) => {
|
||||
cart.value.payType = index;
|
||||
onPay();
|
||||
}
|
||||
|
||||
// 统一下单
|
||||
const onPay = () => {
|
||||
visible.value = true;
|
||||
active.value = 3;
|
||||
useClientRequest<ApiResult<any>>(`/shop/shop-order/createOrder`, {
|
||||
method: 'POST',
|
||||
body: cart.value
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
ElMessage.success(res.message)
|
||||
} else {
|
||||
return ElMessage.error(res.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const onSubmit = async () => {
|
||||
const {data: modify} = await useServerRequest<ApiResult<User>>('/auth/user', {
|
||||
baseURL: runtimeConfig.public.apiServer,
|
||||
method: 'put',
|
||||
body: form
|
||||
})
|
||||
if (modify.value?.code == 0) {
|
||||
ElMessage.success('修改成功')
|
||||
}
|
||||
}
|
||||
|
||||
const reload = async () => {
|
||||
// 要求登录
|
||||
if (!token.value || token.value == '') {
|
||||
navigateTo(`/passport/login`)
|
||||
return false;
|
||||
}
|
||||
const {data: response} = await useServerRequest<ApiResult<CmsProduct>>(`/cms/cms-product/${getIdBySpm(5)}`)
|
||||
console.log(response.value?.data,'response')
|
||||
if (response.value?.data) {
|
||||
assignFields(response.value?.data);
|
||||
form.categoryName = '选择套餐';
|
||||
if(response.value?.data.price){
|
||||
cart.value.payPrice = response.value?.data.price;
|
||||
cart.value.comments = `${response.value?.data.title}`;
|
||||
if(cart.value.num){
|
||||
cart.value.totalPrice = response.value?.data.price * cart.value.num
|
||||
}
|
||||
let goodsName = '';
|
||||
// if(form.type == 0){
|
||||
// goodsName = `【软件】${form.title}`
|
||||
// }
|
||||
// if(form.type == 1){
|
||||
// goodsName = `【插件】${form.title}`
|
||||
// }
|
||||
// cart.value.orderProduct?.push({
|
||||
// goodsId: form.productId,
|
||||
// goodsName: goodsName,
|
||||
// image: form.image,
|
||||
// price: response.value?.data.price,
|
||||
// totalPrice: cart.value.totalPrice,
|
||||
// num: cart.value.num,
|
||||
// month: cart.value?.month,
|
||||
// payType: cart.value.payType
|
||||
// });
|
||||
}
|
||||
useHead({
|
||||
title: `${form.title}`,
|
||||
meta: [{name: website.value.keywords, content: website.value.comments}]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 扫码支付
|
||||
const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
ElMessage.error('还没有评分哦!')
|
||||
// if (form.rate === 0) {
|
||||
// ElMessage.error('还没有评分哦!')
|
||||
// return false;
|
||||
// }
|
||||
// form.productId = props.productId;
|
||||
// useClientRequest<ApiResult<any>>(`/cms/cms-product-comment/`, {
|
||||
// method: 'POST',
|
||||
// body: form
|
||||
// }).then(res => {
|
||||
// if (res.code == 0) {
|
||||
// ElMessage.success(res.message)
|
||||
// visible.value = false
|
||||
// resetFields();
|
||||
// emit('done',1)
|
||||
// } else {
|
||||
// return ElMessage.error(res.message)
|
||||
// }
|
||||
// })
|
||||
}
|
||||
|
||||
// 完成支付
|
||||
const onDone = () => {
|
||||
useClientRequest<ApiResult<any>>(`/system/menu/install`, {
|
||||
method: 'POST',
|
||||
body: {
|
||||
productId: getIdBySpm(5)
|
||||
}
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
ElMessage.success(res.message)
|
||||
} else {
|
||||
return ElMessage.error(res.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
activeIndex.value = path;
|
||||
console.log(path, '=>Path')
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.text-a123 {
|
||||
color: #f0f2f5;
|
||||
}
|
||||
</style>
|
||||
25
pages/product/createWebsite/index.vue
Normal file
25
pages/product/createWebsite/index.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
// 创建站点
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult} from "~/api";
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
|
||||
const {data: website} = await useServerRequest<ApiResult<Company>>('/cms/website/createWebsite', {
|
||||
baseURL: 'http://127.0.0.1:9002/api',
|
||||
method: 'post',
|
||||
body: company.value?.data
|
||||
})
|
||||
if (website.value?.code == 401) {
|
||||
console.log('网站不存在', website.value)
|
||||
}
|
||||
if (website.value?.code == 0) {
|
||||
console.log('网站存在', website.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
25
pages/product/createWebsite/index2.vue
Normal file
25
pages/product/createWebsite/index2.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
// 创建站点
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult} from "~/api";
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
|
||||
const {data: website} = await useServerRequest<ApiResult<Company>>('/cms/website/createWebsite', {
|
||||
baseURL: 'http://127.0.0.1:9002/api',
|
||||
method: 'post',
|
||||
body: company.value?.data
|
||||
})
|
||||
if (website.value?.code == 401) {
|
||||
console.log('网站不存在', website.value)
|
||||
}
|
||||
if (website.value?.code == 0) {
|
||||
console.log('网站存在', website.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
168
pages/product/detail/[id].vue
Normal file
168
pages/product/detail/[id].vue
Normal file
@@ -0,0 +1,168 @@
|
||||
<template>
|
||||
<el-affix :offset="0" @change="onAffix">
|
||||
<div class="affix justify-between p-2 opacity-90 border-b-solid border-gray-200 border-b-1" :class="affix ? 'bg-white w-full' : 'hidden'">
|
||||
<div class="w-3/4 m-auto flex justify-between">
|
||||
<a class="goods-name text-xl font-bold cursor-pointer hover:text-gray-900">{{ goods?.goodsName }}</a>
|
||||
<div class="affix-bar">
|
||||
<el-anchor :offset="100" direction="horizontal" :marker="false">
|
||||
<el-anchor-link :href="`#basic`">
|
||||
参数信息
|
||||
</el-anchor-link>
|
||||
<el-anchor-link :href="`#photo`">
|
||||
图文详情
|
||||
</el-anchor-link>
|
||||
<el-anchor-link :href="`#comment`">
|
||||
用户评价
|
||||
</el-anchor-link>
|
||||
<el-anchor-link :href="`#buynow`">
|
||||
<el-button type="danger" size="small">立即购买</el-button>
|
||||
</el-anchor-link>
|
||||
</el-anchor>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-affix>
|
||||
|
||||
<div id="buynow" class="flex flex-col w-full md:w-screen-xl m-auto md:pt-[60px]" v-if="goods">
|
||||
|
||||
<Breadcrumb :data="goods" />
|
||||
|
||||
<div class="content bg-white rounded-xl">
|
||||
|
||||
<!-- <ProductShopInfo :data="goods?.merchant" />-->
|
||||
|
||||
<div id="buynow" class="bg-white p-4 mt-4 flex gap-xl rounded-xl">
|
||||
<div class="goods-image flex gap-xl">
|
||||
<div class="gap-xs flex flex-col" v-if="goods?.files">
|
||||
<el-avatar v-for="item in JSON.parse(goods.files)" :src="item.url" size="large" shape="square" />
|
||||
</div>
|
||||
<el-image :src="goods?.image" fit="contain" class="w-2xl h-2xl bg-gray-100 border-radius:30px"></el-image>
|
||||
</div>
|
||||
<div class="goods-info flex flex-col gap-xs">
|
||||
<div class="goods-name text-2xl">{{ goods.goodsName }}</div>
|
||||
<div class="goods-price text-2xl red">¥{{ Number(goods?.salePrice) * Number(goods?.num) }}</div>
|
||||
<div class="text-green-7">购买得积分</div>
|
||||
<div class="text-gray-4">配送:无需配送</div>
|
||||
<div class="text-gray-4">保障:假一赔四 退货包运费 极速退款</div>
|
||||
<div class="text-gray-4">销量: {{ goods.sales }}</div>
|
||||
<!-- <template v-for="spec in goods?.goodsSpecValue">-->
|
||||
<!-- <div class="flex items-center">-->
|
||||
<!-- <div class="text-gray-4">{{ spec.value }}:</div>-->
|
||||
<!-- <el-radio-group v-model="goods.radio">-->
|
||||
<!-- <el-radio v-for="(specValue,specIndex) in spec.detail" :label="specIndex" border>{{ specValue }}</el-radio>-->
|
||||
<!-- </el-radio-group>-->
|
||||
<!-- </div>-->
|
||||
<!-- </template>-->
|
||||
<div class="text-gray-4">
|
||||
已选中:{{ goods.radio }}
|
||||
</div>
|
||||
<div class="text-gray-4">
|
||||
数量:
|
||||
<el-input-number v-model="goods.num" :min="1" :max="10" label="描述文字"></el-input-number>
|
||||
</div>
|
||||
<div class="py-5">
|
||||
<el-button-group size="large">
|
||||
<el-button type="danger">立即购买</el-button>
|
||||
<el-button type="warning">加入购物车</el-button>
|
||||
</el-button-group>
|
||||
<el-button size="large" class="ml-3">收藏</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="basic" class="bg-white p-4 mt-4 flex gap-xl rounded-xl">
|
||||
<el-descriptions class="margin-top" title="参数信息" :column="1" border>
|
||||
<el-descriptions-item label="品牌">websoft</el-descriptions-item>
|
||||
<el-descriptions-item label="版本">2.0</el-descriptions-item>
|
||||
<el-descriptions-item label="开发语言">Java、Vue3、Nuxt</el-descriptions-item>
|
||||
<el-descriptions-item label="版本">
|
||||
<el-tag size="small">授权版</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="备注">江苏省苏州市吴中区吴中大道 1188 号</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
|
||||
<div id="photo" class="bg-white p-4 mt-4 flex gap-xl rounded-xl flex-col">
|
||||
<div class="font-bold">图文详情</div>
|
||||
<div class="files flex flex-col w-3/4" v-if="goods?.files">
|
||||
<el-image v-for="item in JSON.parse(goods.files)" :src="item.url" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="comment" class="bg-white p-4 mt-4 flex gap-xl rounded-xl">
|
||||
<div class="font-bold">用户评价</div>
|
||||
</div>
|
||||
|
||||
<div v-if="!goods">
|
||||
<el-empty description="404 该商品找不到了222"></el-empty>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {Goods} from "~/api/shop/goods/model";
|
||||
import Breadcrumb from "~/components/Breadcrumb.vue";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import {useProductAffix} from "~/composables/configState";
|
||||
import {getIdBySpm} from "~/utils/common";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const affix = useProductAffix();
|
||||
|
||||
// 商品ID
|
||||
const goodsId = ref();
|
||||
// 页面信息
|
||||
const form = ref<Navigation>();
|
||||
// 商品信息
|
||||
const goods = ref<Goods>();
|
||||
|
||||
const onAffix = (index: boolean) => {
|
||||
affix.value = index;
|
||||
}
|
||||
|
||||
// 加载数据
|
||||
const reload = async () => {
|
||||
// TODO 请求导航页面数据
|
||||
// const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/navigation/getNavigationByPath',{
|
||||
// query: {
|
||||
// path: goodsId.value
|
||||
// }
|
||||
// })
|
||||
// if(nav.value?.data){
|
||||
// form.value = nav.value.data;
|
||||
// }
|
||||
|
||||
// TODO 获取商品详情
|
||||
console.log(goodsId.value,'sss')
|
||||
const { data: info } = await useServerRequest<ApiResult<Goods>>('/shop/goods/' + getIdBySpm(5))
|
||||
|
||||
goods.value = info.value?.data;
|
||||
console.log(goods.value)
|
||||
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.query.spm,
|
||||
(spm) => {
|
||||
console.log(spm)
|
||||
// TODO 方案一:从spm参数提取商品ID
|
||||
const spmValue = String(spm).split('.')
|
||||
if(spmValue[5]){
|
||||
goodsId.value = spmValue[5];
|
||||
}
|
||||
console.log(goodsId.value)
|
||||
// TODO 方案二(优先级更高):从params获取商品ID
|
||||
const { detail } = route.params
|
||||
const split = String(detail).split('.');
|
||||
if(Number(split[0]) > 0){
|
||||
goodsId.value = split[0];
|
||||
}
|
||||
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
75
pages/product/index.vue
Normal file
75
pages/product/index.vue
Normal file
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<PageBanner :layout="layout" :title="`${form?.categoryName}`" :desc="`${form?.comments}`" />
|
||||
<CardList :param="{type: 0}" :data="data" :disabled="disabled" @done="onSearch" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import type {Company, CompanyParam} from "~/api/system/company/model";
|
||||
import CardList from './components/CardList.vue';
|
||||
import {getIdBySpm} from "~/utils/common";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const data = ref<PageResult<Company>>();
|
||||
const page = ref<number>(0);
|
||||
const resultText = ref('');
|
||||
const layout = ref<any>();
|
||||
const disabled = ref<boolean>(false);
|
||||
|
||||
// 获取状态
|
||||
const form = ref<Navigation>();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
const onSearch = (index: number) => {
|
||||
page.value = index;
|
||||
reload();
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Company>>>('/system/company/page',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value,
|
||||
limit: 12,
|
||||
categoryId: getIdBySpm(5),
|
||||
keywords: where.keywords
|
||||
}})
|
||||
if(response.value?.data){
|
||||
data.value = response.value?.data;
|
||||
}
|
||||
}
|
||||
|
||||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>(`/cms/cms-navigation/${getIdBySpm(5)}`)
|
||||
if(nav.value?.data){
|
||||
form.value = nav.value?.data;
|
||||
useHead({
|
||||
title: `${form.value.title} - WEBSOFT`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
}
|
||||
// 页面布局
|
||||
if(form.value?.layout){
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
|
||||
|
||||
|
||||
watch(
|
||||
() => getIdBySpm(5),
|
||||
(id) => {
|
||||
console.log(id,'id = .>>>>')
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
138
pages/search/index.vue
Normal file
138
pages/search/index.vue
Normal file
@@ -0,0 +1,138 @@
|
||||
<template>
|
||||
<div class="login-layout mt-[100px] min-h-2xl m-auto sm:w-screen-sm w-full">
|
||||
<div class="title text-xl text-gray-700 px-4 py-2 font-500 text-center">
|
||||
<div class="sm:w-screen-sm w-full">
|
||||
<el-input
|
||||
v-model="where.keywords"
|
||||
class="w-full"
|
||||
size="large"
|
||||
placeholder="搜索"
|
||||
:prefix-icon="Search"
|
||||
@keydown.enter="handleClick"
|
||||
>
|
||||
<template #append>
|
||||
<el-button size="large" type="primary" @click="handleClick">搜索</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-tabs v-model="activeName" class="my-3" @tab-click="handleClick">
|
||||
<el-tab-pane label="应用" name="web"></el-tab-pane>
|
||||
<el-tab-pane label="公众号" name="mp-official"></el-tab-pane>
|
||||
<el-tab-pane label="小程序" name="mp-weixin"></el-tab-pane>
|
||||
<el-tab-pane label="移动应用" name="app"></el-tab-pane>
|
||||
<el-tab-pane label="小商店" name="mp-shop"></el-tab-pane>
|
||||
<el-tab-pane label="其他" name="other"></el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="activeName === 'web'">
|
||||
<div class="search bg-white rounded-lg p-3 w-full" v-if="websites.length > 0">
|
||||
<div class="title text-gray-400 px-4 py-2 mb-4">网站应用</div>
|
||||
<div class="result px-3">
|
||||
<div v-for="(item,index) in websites" :key="index" class="app-item block border-solid rounded-lg border-gray-300 border-1 mb-4 p-3 flex flex-row items-center hover:border-blue-4 hover:border-2 cursor-pointer">
|
||||
<el-space @click="navTo(item)">
|
||||
<div class="avatar">
|
||||
<el-avatar :src="item.websiteLogo" style="background-color: #f3f3f3" size="large" />
|
||||
</div>
|
||||
<div class="app-info flex flex-col">
|
||||
<div class="text-lg">{{ item.websiteName }}</div>
|
||||
<div class="text-gray-400">{{ item.comments }}</div>
|
||||
<div class="text-gray-300 text-xs-1">{{ item.websiteType }}</div>
|
||||
</div>
|
||||
</el-space>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else class="px-1 text-center text-gray-500 min-h-xs">
|
||||
{{ resultText }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import {getPath} from "~/utils/common";
|
||||
import type {CompanyParam} from "~/api/system/company/model";
|
||||
import type {Tenant} from "~/api/system/tenant/model";
|
||||
import {navigateTo} from "#imports";
|
||||
import type {Website} from "~/api/cms/website/model";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const list = ref<Tenant[]>([]);
|
||||
const websites = ref<Website[]>([]);
|
||||
const activeName = ref('web');
|
||||
const page = ref<number>(0);
|
||||
const resultText = ref('');
|
||||
const layout = ref<any>();
|
||||
|
||||
// 获取状态
|
||||
const form = ref<Navigation>();
|
||||
const website = useWebsite();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
const navTo = (item: Website) => {
|
||||
if(item.domain){
|
||||
openSpmUrl(`https://${item.domain}`,item,item.tenantId)
|
||||
return;
|
||||
}
|
||||
openSpmUrl(`https://${item.websiteCode}.wsdns.cn`,item,item.tenantId)
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
|
||||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/getNavigationByPath',{query: {path: getPath()}})
|
||||
if(nav.value?.data){
|
||||
form.value = nav.value?.data;
|
||||
}
|
||||
// 页面布局
|
||||
if(form.value?.layout){
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
useHead({
|
||||
title: `搜索结果 - ${website.value.websiteName}`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 搜索结果
|
||||
const handleClick = async () => {
|
||||
if (where.keywords == '') {
|
||||
return false;
|
||||
}
|
||||
if(activeName.value == 'web'){
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Website>>>('/cms/cms-website/page',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value, keywords: where.keywords
|
||||
}})
|
||||
if(response.value?.data){
|
||||
if (response.value?.data.list) {
|
||||
websites.value = response.value?.data.list;
|
||||
}
|
||||
if(response.value.data.count == 0){
|
||||
resultText.value = '暂无相关结果'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route,
|
||||
() => {
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
</script>
|
||||
57
pages/templates/components/CardList.vue
Normal file
57
pages/templates/components/CardList.vue
Normal file
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative" v-infinite-scroll="load">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer">
|
||||
<el-image
|
||||
:src="item.image"
|
||||
:fit="fit" :lazy="true" class="w-full md:h-[150px] h-[199px] cursor-pointer bg-gray-50"/>
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col gap-1.5">
|
||||
<div class="flex-1 text-xl cursor-pointer flex items-center">
|
||||
{{ item.shortName }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500 line-clamp-2">{{ item.comments }}</div>
|
||||
</div>
|
||||
<div class="button-group flex justify-between items-center mt-3">
|
||||
<el-button class="w-full" @click="openSpmUrl(`https://${item.tenantId}.wsdns.cn`,item,item.companyId,true)">预览</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="disabled" class="px-1 text-center text-gray-500 min-h-xs">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
list?: Company[];
|
||||
disabled?: boolean;
|
||||
fit?: any;
|
||||
}>(),
|
||||
{
|
||||
fit: 'cover'
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const load = () => {
|
||||
if(!props.disabled){
|
||||
emit('done')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
115
pages/templates/components/PageBanner.vue
Normal file
115
pages/templates/components/PageBanner.vue
Normal file
@@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<div class="banner m-auto relative sm:flex">
|
||||
<svg viewBox="0 0 1440 181" fill="none" xmlns="http://www.w3.org/2000/svg"
|
||||
class="pointer-events-none absolute w-full top-[-2px] transition-all text-green-5 flex-shrink-0 opacity-100 duration-[400ms] opacity-80 -z-10">
|
||||
<mask id="path-1-inside-1_414_5526" fill="white">
|
||||
<path d="M0 0H1440V181H0V0Z"></path>
|
||||
</mask>
|
||||
<path d="M0 0H1440V181H0V0Z" fill="url(#paint0_linear_414_5526)" fill-opacity="0.22"></path>
|
||||
<path d="M0 2H1440V-2H0V2Z" fill="url(#paint1_linear_414_5526)" mask="url(#path-1-inside-1_414_5526)"></path>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_414_5526" x1="720" y1="0" x2="720" y2="181" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_414_5526" x1="0" y1="90.5" x2="1440" y2="90.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor" stop-opacity="0"></stop>
|
||||
<stop offset="0.395" stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<div class="md:w-screen-xl m-auto md:p-0 px-4">
|
||||
<div class="py-8 sm:py-16" _path="/templates" _dir="" _draft="false" _partial="false" _locale=""
|
||||
_id="content:4.templates.yml" _type="yaml" _source="content" _file="4.templates.yml" _stem="4.templates"
|
||||
_extension="yml">
|
||||
<div class="gap-8 sm:gap-y-16 grid lg:grid-cols-2 lg:items-center" v-if="layout">
|
||||
<div class="">
|
||||
<h1
|
||||
class="text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-4xl lg:text-5xl">
|
||||
<span v-if="layout.title">{{ layout.title }}</span>
|
||||
<span v-if="layout.name">{{ layout.name }}</span>
|
||||
</h1>
|
||||
|
||||
<div class="mt-4 text-lg text-gray-500 dark:text-gray-400">
|
||||
{{ layout.description }}
|
||||
</div>
|
||||
<div class="flex justify-between w-full items-center mt-4">
|
||||
<el-space>
|
||||
<div class="w-[500px]">
|
||||
<el-input
|
||||
v-model="where.keywords"
|
||||
placeholder="搜索"
|
||||
:prefix-icon="Search"
|
||||
@keydown.enter="handleClick"
|
||||
>
|
||||
<template #append>
|
||||
<el-button size="large" type="primary" @click="handleClick">搜索</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<el-button
|
||||
size="large"
|
||||
@click="openSpmUrl(`/passport/login`)"
|
||||
>
|
||||
创建
|
||||
</el-button>
|
||||
</el-space>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import {useConfigInfo} from "~/composables/configState";
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import type {CompanyParam} from "~/api/system/company/model";
|
||||
|
||||
const token = useToken();
|
||||
const sysDomain = useSysDomain();
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
layout?: any;
|
||||
title?: string;
|
||||
desc?: string;
|
||||
buyUrl?: string;
|
||||
}>(),
|
||||
{
|
||||
title: 'Templates',
|
||||
desc: 'Explore community templates to get up and running in a few seconds.',
|
||||
demoUrl: '/product/website',
|
||||
buyUrl: 'https://github.com/websoft9/ansible-templates'
|
||||
}
|
||||
);
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
const config = useConfigInfo();
|
||||
// 搜索结果
|
||||
const handleClick = async () => {
|
||||
if (where.keywords == '') {
|
||||
return false;
|
||||
}
|
||||
if(activeName.value == 'web'){
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Website>>>('/cms/cms-website/page',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value, keywords: where.keywords
|
||||
}})
|
||||
if(response.value?.data){
|
||||
if (response.value?.data.list) {
|
||||
websites.value = response.value?.data.list;
|
||||
}
|
||||
if(response.value.data.count == 0){
|
||||
resultText.value = '暂无相关结果'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
84
pages/templates/index.vue
Normal file
84
pages/templates/index.vue
Normal file
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<PageBanner :layout="layout" :title="`${form?.categoryName}`" :desc="`${form?.comments}`" />
|
||||
<CardList :param="{type: 0,official: true}" :list="list" :disabled="disabled" @done="onSearch" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import type {Company, CompanyParam} from "~/api/system/company/model";
|
||||
import CardList from './components/CardList.vue';
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 页面信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const list = ref<Company[]>([]);
|
||||
const page = ref<number>(1);
|
||||
const resultText = ref('');
|
||||
const layout = ref<any>();
|
||||
const disabled = ref<boolean>(false);
|
||||
|
||||
// 获取状态
|
||||
const form = ref<Navigation>();
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
const onSearch = () => {
|
||||
if(!disabled.value){
|
||||
page.value++;
|
||||
reload(route.path);
|
||||
}
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
const reload = async (path: string) => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Company>>>('/system/company/pageAll',{baseURL: runtimeConfig.public.apiServer, params: {
|
||||
page: page.value,
|
||||
limit: 8,
|
||||
categoryId: getIdBySpm(5),
|
||||
keywords: where.keywords
|
||||
}})
|
||||
if(response.value?.data){
|
||||
if (list.value.length < response.value?.data.count) {
|
||||
disabled.value = false;
|
||||
if (response.value?.data.list) {
|
||||
list.value = list.value.concat(response.value?.data.list);
|
||||
}
|
||||
}else {
|
||||
disabled.value = true;
|
||||
}
|
||||
if(response.value.data.count == 0){
|
||||
resultText.value = '暂无相关结果'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>(`/cms/cms-navigation/${getIdBySpm(5)}`)
|
||||
if(nav.value?.data){
|
||||
form.value = nav.value?.data;
|
||||
useHead({
|
||||
title: `${form.value.title} - WEBSOFT`,
|
||||
bodyAttrs: {
|
||||
class: "page-container",
|
||||
}
|
||||
});
|
||||
}
|
||||
// 页面布局
|
||||
if(form.value?.layout){
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
|
||||
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
reload(path);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
58
pages/user/auth.vue
Normal file
58
pages/user/auth.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<PageBanner title="实名认证" desc="Authentication"/>
|
||||
<div class="login-layout m-auto sm:w-screen-xl w-full">
|
||||
<div class="m-auto flex sm:flex-row flex-col sm:px-0 px-3 ">
|
||||
<!-- 用户菜单 -->
|
||||
<UserMenu :activeIndex="activeIndex" @done="reload"/>
|
||||
<div class="flash bg-white rounded-lg px-8 py-4 w-full">
|
||||
<Auth @done="reload"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {useConfigInfo, useWebsite} from "~/composables/configState";
|
||||
import {ref} from 'vue'
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult} from "~/api";
|
||||
import UserMenu from "./components/UserMenu.vue";
|
||||
import Auth from './components/Auth.vue';
|
||||
import type {ShopMerchantApply} from "~/api/shop/shopMerchantApply/model";
|
||||
import Base from "~/pages/user/components/Base.vue";
|
||||
|
||||
// 配置信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const route = useRoute();
|
||||
const activeIndex = ref('');
|
||||
const website = useWebsite()
|
||||
const config = useConfigInfo();
|
||||
const merchantApply = ref<ShopMerchantApply>();
|
||||
|
||||
const reload = async () => {
|
||||
// 未登录状态(是否强制登录)
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token || token == '') {
|
||||
navigateTo('/passport/login');
|
||||
return false;
|
||||
}
|
||||
const {data: response} = await useServerRequest<ApiResult<ShopMerchantApply>>('/shop/shop-merchant-apply/getByUserId', {baseURL: runtimeConfig.public.apiServer})
|
||||
if (response.value?.data) {
|
||||
merchantApply.value = response.value.data;
|
||||
}
|
||||
if(config.value){
|
||||
useHead({
|
||||
title: `实名认证 - ${config.value?.siteName}`,
|
||||
meta: [{name: website.value.keywords, content: website.value.comments}]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
activeIndex.value = path;
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
460
pages/user/components/Auth.vue
Normal file
460
pages/user/components/Auth.vue
Normal file
@@ -0,0 +1,460 @@
|
||||
<template>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
v-loading="loading"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-position="top"
|
||||
class="w-full sm:py-2"
|
||||
size="large"
|
||||
status-icon
|
||||
>
|
||||
<el-tabs v-model="form.type" class="flash bg-white ml-0">
|
||||
<el-tab-pane :name="0" label="个人认证"/>
|
||||
<el-tab-pane :name="1" label="企业认证"/>
|
||||
</el-tabs>
|
||||
<!-- 已完成认证 -->
|
||||
<template v-if="form.status === 1">
|
||||
<template v-if="form.type == 0">
|
||||
<el-result
|
||||
icon="success"
|
||||
title="个人认证已通过"
|
||||
:sub-title="`认证完成时间 ${form.completedTime}`"
|
||||
>
|
||||
<template #extra>
|
||||
<el-button type="text" @click="onUpdate">修改认证信息</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-result
|
||||
icon="success"
|
||||
title="企业认证已通过"
|
||||
:sub-title="`认证完成时间 ${form.completedTime}`"
|
||||
>
|
||||
<template #extra>
|
||||
<el-button type="text" @click="onUpdate">修改认证信息</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
</template>
|
||||
</template>
|
||||
<!-- 申请被驳回 -->
|
||||
<template v-if="form.status === 2">
|
||||
<el-result
|
||||
icon="error"
|
||||
title="您的申请已被驳回"
|
||||
:sub-title="`${form.reason}`"
|
||||
>
|
||||
<template #extra>
|
||||
<el-button type="text" @click="onUpdate">修改认证信息</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
</template>
|
||||
<!-- 未完成认证 -->
|
||||
<template v-if="form.status === 0 && form.checkStatus">
|
||||
<el-result
|
||||
icon="warning"
|
||||
title="审核中"
|
||||
:sub-title="`您的申请已提交,请耐心等待工作人员的审核,非常感谢`"
|
||||
>
|
||||
</el-result>
|
||||
</template>
|
||||
<template v-if="form.status === 0 && !form.checkStatus">
|
||||
<template v-if="form.type == 1">
|
||||
<el-form-item label="企业名称" prop="merchantName">
|
||||
<el-input v-model="form.merchantName" placeholder="请输入企业名称"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="社会信用代码" prop="merchantCode">
|
||||
<el-input v-model="form.merchantCode" placeholder="请输入社会信用代码"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="营业执照" required>
|
||||
<el-upload
|
||||
v-model:file-list="yyzzFile"
|
||||
action="https://common-api.websoft.top/api/oss/upload"
|
||||
:headers="{
|
||||
Authorization: token,
|
||||
TenantId: tenantId,
|
||||
}"
|
||||
:limit="1"
|
||||
list-type="picture-card"
|
||||
:on-preview="handlePictureCardPreview"
|
||||
:on-remove="yyzzRemove"
|
||||
:on-success="yyzzOnSuccess"
|
||||
>
|
||||
<el-icon><Plus /></el-icon>
|
||||
</el-upload>
|
||||
|
||||
<el-dialog v-model="dialogVisible">
|
||||
<div class="flex justify-center">
|
||||
<img w-full :src="dialogImageUrl" alt="Preview Image" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
</el-form-item>
|
||||
<el-form-item label="门头照片">
|
||||
<el-upload
|
||||
v-model:file-list="image"
|
||||
action="https://common-api.websoft.top/api/oss/upload"
|
||||
:headers="{
|
||||
Authorization: token,
|
||||
TenantId: tenantId,
|
||||
}"
|
||||
:limit="1"
|
||||
list-type="picture-card"
|
||||
:on-preview="handlePictureCardPreview"
|
||||
:on-remove="imageRemove"
|
||||
:on-success="imageOnSuccess"
|
||||
>
|
||||
<el-icon><Plus /></el-icon>
|
||||
</el-upload>
|
||||
|
||||
<el-dialog v-model="dialogVisible">
|
||||
<div class="flex justify-center">
|
||||
<img w-full :src="dialogImageUrl" alt="Preview Image" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
</el-form-item>
|
||||
<el-form-item label="其他证件">
|
||||
<el-upload
|
||||
v-model:file-list="files"
|
||||
action="https://common-api.websoft.top/api/oss/upload"
|
||||
:headers="{
|
||||
Authorization: token,
|
||||
TenantId: tenantId,
|
||||
}"
|
||||
:limit="9"
|
||||
list-type="picture-card"
|
||||
:on-preview="handlePictureCardPreview"
|
||||
:on-remove="filesRemove"
|
||||
:on-success="filesOnSuccess"
|
||||
>
|
||||
<el-icon><Plus /></el-icon>
|
||||
</el-upload>
|
||||
|
||||
<el-dialog v-model="dialogVisible">
|
||||
<div class="flex justify-center">
|
||||
<img w-full :src="dialogImageUrl" alt="Preview Image" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
</el-form-item>
|
||||
</template>
|
||||
<el-form-item label="真实姓名" prop="realName">
|
||||
<el-input v-model="form.realName" placeholder="请输入真实姓名"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="phone">
|
||||
<el-input v-model="form.phone" maxlength="11" placeholder="请输入真实有效的手机号码"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="身份证号码" prop="idCard">
|
||||
<el-input v-model="form.idCard" placeholder="请输入证件号码"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="身份证" required>
|
||||
<el-upload
|
||||
v-model:file-list="sfzFile"
|
||||
action="https://common-api.websoft.top/api/oss/upload"
|
||||
:headers="{
|
||||
Authorization: token,
|
||||
TenantId: tenantId,
|
||||
}"
|
||||
:limit="2"
|
||||
list-type="picture-card"
|
||||
:on-preview="handlePictureCardPreview"
|
||||
:on-remove="sfzRemove"
|
||||
:on-success="sfzSuccess"
|
||||
>
|
||||
<el-icon><Plus /></el-icon>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
<el-form-item label="所属行业" prop="category">
|
||||
<el-cascader
|
||||
v-model="industry"
|
||||
:options="industryData"
|
||||
placeholder="请选择所属行业"
|
||||
class="w-full"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="业务描述" prop="comments">
|
||||
<el-input v-model="form.comments" placeholder="请输入公司业务介绍" :rows="5" type="textarea" />
|
||||
</el-form-item>
|
||||
<el-form-item label="注册协议">
|
||||
<el-checkbox v-model="isAgree">
|
||||
请务必提供真实信息,我司有权自行或委托第三方审查您提供的身份信息是否属真实,有效。若提供虚假信息,由此的全部后果由您承担。
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-space class="flex">
|
||||
<el-button type="primary" size="large" :disabled="!isAgree" @click="submitForm(formRef)">
|
||||
{{ isUpdate ? '提交修改' : '提交申请' }}
|
||||
</el-button>
|
||||
</el-space>
|
||||
</template>
|
||||
</el-form>
|
||||
<el-dialog v-model="dialogVisible">
|
||||
<div class="flex justify-center">
|
||||
<el-image w-full :src="dialogImageUrl" alt="查看证件" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {reactive, ref} from 'vue'
|
||||
import {Plus} from '@element-plus/icons-vue'
|
||||
import type {FormInstance, FormRules, UploadProps, UploadUserFile} from 'element-plus'
|
||||
import type {ShopMerchantApply} from "~/api/shop/shopMerchantApply/model";
|
||||
import industryData from '@/assets/json/industry-data.json';
|
||||
import {useClientRequest} from "~/composables/useClientRequest";
|
||||
import type {ApiResult} from "~/api";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ShopMerchant} from "~/api/shop/shopMerchant/model";
|
||||
import useFormData from "~/utils/use-form-data";
|
||||
|
||||
const token = useToken();
|
||||
const tenantId = localStorage.getItem('TID_ADMIN')
|
||||
const formRef = ref<FormInstance>()
|
||||
const yyzzFile = ref<UploadUserFile[]>([])
|
||||
const sfzFile = ref<UploadUserFile[]>([])
|
||||
const sfzStr = ref<string[]>([]);
|
||||
const files = ref<UploadUserFile[]>([])
|
||||
const filesStr = ref<string[]>([])
|
||||
const image = ref<UploadUserFile[]>([])
|
||||
const industry = ref<any[]>([])
|
||||
const loading = ref<boolean>(true)
|
||||
const isUpdate = ref<boolean>(false)
|
||||
const isAgree = ref<boolean>(false)
|
||||
|
||||
const dialogImageUrl = ref('')
|
||||
const dialogVisible = ref(false)
|
||||
|
||||
const {form, assignFields, resetFields} = useFormData<ShopMerchantApply>({
|
||||
applyId: undefined,
|
||||
type: 0,
|
||||
merchantName: undefined,
|
||||
merchantCode: undefined,
|
||||
image: undefined,
|
||||
phone: undefined,
|
||||
realName: undefined,
|
||||
idCard: undefined,
|
||||
sfz1: undefined,
|
||||
sfz2: undefined,
|
||||
yyzz: undefined,
|
||||
name2: undefined,
|
||||
shopType: undefined,
|
||||
parentId: undefined,
|
||||
categoryId: undefined,
|
||||
category: undefined,
|
||||
commission: undefined,
|
||||
keywords: undefined,
|
||||
files: undefined,
|
||||
ownStore: undefined,
|
||||
recommend: undefined,
|
||||
completedTime: undefined,
|
||||
goodsReview: undefined,
|
||||
userId: undefined,
|
||||
comments: undefined,
|
||||
reason: undefined,
|
||||
checkStatus: undefined,
|
||||
status: 0,
|
||||
sortNumber: undefined
|
||||
})
|
||||
|
||||
const rules = reactive<FormRules<ShopMerchantApply>>({
|
||||
realName: [
|
||||
{required: true, message: '请输入真实姓名', trigger: 'blur'},
|
||||
{min: 2, max: 5, message: '长度应为2-5个字符', trigger: 'blur'},
|
||||
],
|
||||
phone: [
|
||||
{required: true, message: '请输入手机号码', trigger: 'blur'},
|
||||
{pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur'},
|
||||
],
|
||||
idCard: [
|
||||
{required: true, message: '请输入证件号码', trigger: 'blur'},
|
||||
{min: 18, max: 18, message: '证件号码长度应为18位', trigger: 'blur'},
|
||||
],
|
||||
merchantName: [
|
||||
{required: true, message: '请输入企业名称', trigger: 'blur'}
|
||||
],
|
||||
merchantCode: [
|
||||
{required: true, message: '请输入社会信用代码', trigger: 'blur'}
|
||||
],
|
||||
category: [
|
||||
{required: true, message: '请选择所属行业', trigger: 'change'}
|
||||
]
|
||||
})
|
||||
|
||||
const yyzzRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
|
||||
form.yyzz = '';
|
||||
}
|
||||
|
||||
const yyzzOnSuccess = (e: any) => {
|
||||
form.yyzz = e.data.downloadUrl
|
||||
}
|
||||
|
||||
const sfzRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
|
||||
form.sfz1 = '';
|
||||
form.sfz2 = '';
|
||||
}
|
||||
|
||||
const sfzSuccess = (e:any) => {
|
||||
sfzStr.value.push(e.data.downloadUrl)
|
||||
}
|
||||
|
||||
const imageOnSuccess = (e: any) => {
|
||||
form.image = e.data.downloadUrl
|
||||
}
|
||||
|
||||
const imageRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
|
||||
form.image = '';
|
||||
}
|
||||
|
||||
const filesRemove: UploadProps['onRemove'] = (uploadFile) => {
|
||||
const index = filesStr.value.findIndex(f => f == uploadFile.url);
|
||||
filesStr.value.splice(index, 1)
|
||||
}
|
||||
|
||||
const filesOnSuccess = (e: any) => {
|
||||
filesStr.value.push(e.data.downloadUrl)
|
||||
}
|
||||
|
||||
// 所属行业
|
||||
const handleChange = (value: any) => {
|
||||
let parent = ''
|
||||
let category = ''
|
||||
industryData.map(d => {
|
||||
if (d.value == value[0]) {
|
||||
form.parentId = d.value
|
||||
parent = d.label
|
||||
if (d.children) {
|
||||
d.children.map(c => {
|
||||
if (c.value == value[1]) {
|
||||
category = c.label
|
||||
form.categoryId = c.value
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
form.category = `${parent}/${category}`
|
||||
}
|
||||
|
||||
const onUpdate = () => {
|
||||
form.status = 0;
|
||||
form.checkStatus = false
|
||||
}
|
||||
|
||||
|
||||
const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
|
||||
dialogImageUrl.value = uploadFile.url!
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid, fields) => {
|
||||
if (valid) {
|
||||
console.log('submit!',valid)
|
||||
if (form.type == 0) {
|
||||
form.shopType = '个人开发者';
|
||||
form.merchantName = form.realName;
|
||||
}
|
||||
if(form.type == 1){
|
||||
form.shopType = '企业开发者';
|
||||
if(form.yyzz == ''){
|
||||
return ElMessage.error('请上营业执照');
|
||||
}
|
||||
if(filesStr.value.length > 0){
|
||||
form.files = JSON.stringify(filesStr.value)
|
||||
}
|
||||
}
|
||||
if(sfzStr.value.length == 1){
|
||||
return ElMessage.error('请上传身份证正反面');
|
||||
}
|
||||
form.sfz1 = sfzStr.value[0];
|
||||
form.sfz2 = sfzStr.value[1];
|
||||
useClientRequest<ApiResult<any>>(`/shop/shop-merchant-apply`, {
|
||||
baseURL: runtimeConfig.public.apiServer,
|
||||
method: isUpdate.value ? 'PUT' : 'POST',
|
||||
body: form
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
ElMessage.success(res.message)
|
||||
reload();
|
||||
} else {
|
||||
return ElMessage.error(res.message)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
console.log('error submit!', fields)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const resetForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
resetFields();
|
||||
}
|
||||
|
||||
const reload = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<ShopMerchant>>('/shop/shop-merchant-apply/getByUserId')
|
||||
if (response.value?.data) {
|
||||
isUpdate.value = true;
|
||||
assignFields(response.value.data)
|
||||
industry.value = []
|
||||
industry.value.push(form.parentId)
|
||||
industry.value.push(form.categoryId)
|
||||
files.value = []
|
||||
filesStr.value = []
|
||||
yyzzFile.value = []
|
||||
sfzFile.value = []
|
||||
sfzStr.value = []
|
||||
image.value = []
|
||||
if(form.sfz1){
|
||||
sfzFile.value.push({
|
||||
uid: 1,
|
||||
url: form.sfz1,
|
||||
name: '身份证正面',
|
||||
})
|
||||
}
|
||||
if(form.sfz2){
|
||||
sfzFile.value.push({
|
||||
uid: 2,
|
||||
url: form.sfz2,
|
||||
name: '身份证反面',
|
||||
})
|
||||
}
|
||||
if(form.yyzz){
|
||||
yyzzFile.value.push({
|
||||
uid: 3,
|
||||
url: form.yyzz,
|
||||
name: '营业执照',
|
||||
})
|
||||
}
|
||||
if(form.image){
|
||||
image.value.push({
|
||||
uid: 4,
|
||||
url: form.image,
|
||||
name: '',
|
||||
})
|
||||
}
|
||||
if (form.files) {
|
||||
const arr = JSON.parse(form.files)
|
||||
let i = 1;
|
||||
arr.map(d => {
|
||||
files.value.push({
|
||||
uid: i++,
|
||||
url: d,
|
||||
name: '',
|
||||
})
|
||||
filesStr.value.push(d)
|
||||
})
|
||||
}
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
reload();
|
||||
</script>
|
||||
<style scoped>
|
||||
/* 自定义 el-dialog 的样式 */
|
||||
.el-dialog {
|
||||
background: rgba(255, 255, 255, 0.5); /* 设置背景颜色为半透明白色 */
|
||||
backdrop-filter: blur(10px); /* 可选:为背景添加模糊效果 */
|
||||
}
|
||||
</style>
|
||||
46
pages/user/components/Base.vue
Normal file
46
pages/user/components/Base.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<el-form :model="form" label-width="auto" size="large" label-position="top">
|
||||
<el-form-item label="租户ID" class="px-4">
|
||||
<el-input disabled v-model="form.tenantId"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" class="px-4">
|
||||
<el-input disabled v-model="form.mobile"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="应用名称" class="px-4">
|
||||
<el-input v-model="form.tenantName"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="昵称" class="px-4">
|
||||
<el-input v-model="form.nickname"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱账号" class="px-4">
|
||||
<el-input v-model="form.email" placeholder="邮箱账号"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别" class="px-4">
|
||||
<el-radio-group v-model="form.sex">
|
||||
<el-radio value="1">男</el-radio>
|
||||
<el-radio value="2">女</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="个人签名" class="px-4">
|
||||
<el-input v-model="form.comments" type="textarea" placeholder="个人签名" :rows="4"/>
|
||||
</el-form-item>
|
||||
<el-form-item class="px-4">
|
||||
<el-button type="primary" class="sm:w-auto w-full" size="large" @click="onSubmit">保存</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
form?: any;
|
||||
title?: string;
|
||||
desc?: string;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
45
pages/user/components/Order.vue
Normal file
45
pages/user/components/Order.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<el-table :data="tableData">
|
||||
<el-table-column prop="orderId" label="订单号" />
|
||||
<el-table-column prop="comments" label="产品名称" />
|
||||
<el-table-column prop="payPrice" label="订单金额(元)" />
|
||||
<el-table-column prop="month" label="购买时长(月)" />
|
||||
<el-table-column prop="appName" label="应用名称" />
|
||||
<el-table-column prop="createTime" label="下单时间" />
|
||||
<el-table-column prop="action" label="操作">
|
||||
<template #default="scope">
|
||||
<el-button @click="openSpmUrl(`https://${scope.row.tenantId}.websoft.top`,form,scope.row.tenantId,true)">控制台</el-button></template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
||||
import {ref} from "vue";
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import type {OrderGoods} from "~/api/system/orderGoods/model";
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
form?: any;
|
||||
title?: string;
|
||||
desc?: string;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const tableData = ref<OrderGoods[]>([]);
|
||||
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<OrderGoods>>>('/system/order-goods/page',{
|
||||
params: {
|
||||
userId: localStorage.getItem('UserId')
|
||||
}
|
||||
})
|
||||
if(response.value?.data){
|
||||
tableData.value = response.value.data.list;
|
||||
console.log(tableData.value,'tableData')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
31
pages/user/components/Password.vue
Normal file
31
pages/user/components/Password.vue
Normal file
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<el-form :model="form" label-width="auto" size="large" label-position="top">
|
||||
<el-form-item label="旧密码" class="px-4">
|
||||
<el-input v-model="form.oldPassword" placeholder="请输入旧密码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="新密码" class="px-4">
|
||||
<el-input v-model="form.password" type="password" placeholder="请输入新密码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="确认密码" class="px-4">
|
||||
<el-input v-model="form.password2" type="password" placeholder="请确认新密码" />
|
||||
</el-form-item>
|
||||
<el-form-item class="px-4">
|
||||
<el-button type="primary" size="large" @click="onSubmit">保存</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
form?: any;
|
||||
title?: string;
|
||||
desc?: string;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
60
pages/user/components/UserMenu.vue
Normal file
60
pages/user/components/UserMenu.vue
Normal file
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<el-space direction="vertical" class="sm:w-[140px] sm:flex sm:mb-0 mb-5 w-full pr-7">
|
||||
<div class="py-2" v-for="(item,index) in activities" :index="`${item.path}`" :key="index">
|
||||
<el-button :icon="item.icon" link class="text-gray-500" plain @click="navigateTo(item.path)">{{ item.name }}</el-button>
|
||||
</div>
|
||||
</el-space>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {User,Lock,Postcard,Tickets,SwitchButton} from '@element-plus/icons-vue'
|
||||
import {navigateTo} from "#imports";
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
layout?: any;
|
||||
activeIndex?: string;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done', index: string): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
const activities = [
|
||||
{
|
||||
icon: User,
|
||||
name: '账号信息',
|
||||
path: '/user'
|
||||
},
|
||||
{
|
||||
icon: Lock,
|
||||
name: '密码修改',
|
||||
path: '/user/password'
|
||||
},
|
||||
{
|
||||
icon: Postcard,
|
||||
name: '实名认证',
|
||||
path: '/user/auth'
|
||||
},
|
||||
{
|
||||
icon: SwitchButton,
|
||||
name: '退出登录',
|
||||
path: '/user/logout'
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
const handleSelect = (index: string) => {
|
||||
emit('done', index)
|
||||
}
|
||||
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.custom-menu-item:hover {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
</style>
|
||||
96
pages/user/index.vue
Normal file
96
pages/user/index.vue
Normal file
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<PageBanner title="账户中心" desc="Account Center"/>
|
||||
<div class="login-layout m-auto sm:w-screen-xl w-full">
|
||||
<div class="m-auto flex sm:flex-row flex-col sm:px-0 px-3">
|
||||
<!-- 用户菜单 -->
|
||||
<UserMenu :activeIndex="activeIndex" @done="onDone" class="sm:flex hidden"/>
|
||||
<div class="flash bg-white rounded-lg w-full">
|
||||
<div class="title text-xl text-gray-700 md:px-8 p-4 md:mt-3 font-500">账号信息</div>
|
||||
<div class="sm:w-screen-md w-full sm:px-4 sm:py-2">
|
||||
<Base :form="form"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
import useFormData from '@/utils/use-form-data';
|
||||
import type {User} from '@/api/system/user/model';
|
||||
import {ref} from 'vue'
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult} from "~/api";
|
||||
import UserMenu from "./components/UserMenu.vue";
|
||||
import Base from './components/Base.vue';
|
||||
|
||||
// 配置信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const route = useRoute();
|
||||
const website = useWebsite()
|
||||
const userInfo = ref<User>();
|
||||
const activeIndex = ref('');
|
||||
|
||||
|
||||
// 配置信息
|
||||
const {form, assignFields} = useFormData<User>({
|
||||
userId: undefined,
|
||||
nickname: '',
|
||||
username: '',
|
||||
phone: '',
|
||||
mobile: '',
|
||||
sex: '',
|
||||
sexName: '',
|
||||
email: '',
|
||||
password: '',
|
||||
code: '',
|
||||
smsCode: '',
|
||||
comments: '',
|
||||
remember: true,
|
||||
tenantId: undefined,
|
||||
tenantName: undefined
|
||||
});
|
||||
|
||||
useHead({
|
||||
title: `用户中心`,
|
||||
meta: [{name: website.value.keywords, content: website.value.comments}]
|
||||
});
|
||||
|
||||
const onDone = (index: string) => {
|
||||
activeIndex.value = index;
|
||||
}
|
||||
|
||||
const onSubmit = async () => {
|
||||
const {data: modify} = await useServerRequest<ApiResult<User>>('/auth/user', {
|
||||
baseURL: runtimeConfig.public.apiServer,
|
||||
method: 'put',
|
||||
body: form
|
||||
})
|
||||
if (modify.value?.code == 0) {
|
||||
ElMessage.success('修改成功')
|
||||
}
|
||||
}
|
||||
|
||||
const reload = async () => {
|
||||
// 未登录状态(是否强制登录)
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token || token == '') {
|
||||
navigateTo('/passport/login');
|
||||
return false;
|
||||
}
|
||||
const {data: response} = await useServerRequest<ApiResult<User>>('/auth/user', {baseURL: runtimeConfig.public.apiServer})
|
||||
if (response.value?.data) {
|
||||
userInfo.value = response.value?.data;
|
||||
assignFields(response.value?.data);
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
activeIndex.value = path;
|
||||
console.log(path, '=>Path')
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
13
pages/user/logout.vue
Normal file
13
pages/user/logout.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {useToken} from "~/composables/configState";
|
||||
|
||||
const token = useToken();
|
||||
token.value = '';
|
||||
localStorage.clear();
|
||||
setTimeout(() => {
|
||||
navigateTo('/')
|
||||
return;
|
||||
}, 1000)
|
||||
</script>
|
||||
94
pages/user/order.vue
Normal file
94
pages/user/order.vue
Normal file
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<PageBanner title="订单列表" desc="Order List" />
|
||||
<div class="login-layout m-auto sm:w-screen-xl w-full">
|
||||
<div class="m-auto flex sm:flex-row flex-col sm:p-0 p-3">
|
||||
<!-- 用户菜单 -->
|
||||
<UserMenu :activeIndex="activeIndex" @done="onDone" />
|
||||
<div class=" bg-white rounded-lg w-full">
|
||||
<div class="title text-xl text-gray-700 md:px-8 p-4 md:mt-3 font-500">订单列表</div>
|
||||
<div class="flash bg-white rounded-lg px-8 py-4 w-auto">
|
||||
<Order :form="form" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {useConfigInfo, useToken, useWebsite} from "~/composables/configState";
|
||||
import useFormData from '@/utils/use-form-data';
|
||||
import type { User } from '@/api/system/user/model';
|
||||
import { ref } from 'vue'
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult} from "~/api";
|
||||
import UserMenu from "./components/UserMenu.vue";
|
||||
import Order from './components/Order.vue';
|
||||
import Auth from "~/pages/user/auth.vue";
|
||||
|
||||
// 配置信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const route = useRoute();
|
||||
const website = useWebsite()
|
||||
const config = useConfigInfo();
|
||||
const token = useToken();
|
||||
const userInfo = ref<User>();
|
||||
const activeIndex = ref('');
|
||||
|
||||
// 配置信息
|
||||
const { form, assignFields } = useFormData<User>({
|
||||
userId: undefined,
|
||||
nickname: '',
|
||||
username: '',
|
||||
phone: '',
|
||||
mobile: '',
|
||||
sex: '',
|
||||
sexName: '',
|
||||
email: '',
|
||||
password: '',
|
||||
code: '',
|
||||
smsCode: '',
|
||||
comments: '',
|
||||
remember: true
|
||||
});
|
||||
|
||||
const tableData = ref<any[]>();
|
||||
|
||||
useHead({
|
||||
title: `用户中心 - ${config.value?.siteName}`,
|
||||
meta: [{ name: website.value.keywords, content: website.value.comments }]
|
||||
});
|
||||
|
||||
const onSubmit = async () => {
|
||||
const {data: modify } = await useServerRequest<ApiResult<User>>('/auth/user',{
|
||||
baseURL: runtimeConfig.public.apiServer,
|
||||
method: 'put',
|
||||
body: form
|
||||
})
|
||||
if(modify.value?.code == 0){
|
||||
ElMessage.success('修改成功')
|
||||
}
|
||||
}
|
||||
|
||||
const reload = async () => {
|
||||
// 未登录状态(是否强制登录)
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token || token == '') {
|
||||
navigateTo('/passport/login');
|
||||
return false;
|
||||
}
|
||||
// const {data: response} = await useServerRequest<ApiResult<Order>>('/system/order',{baseURL: runtimeConfig.public.apiServer})
|
||||
// if(response.value?.data){
|
||||
// console.log(response.value,'order')
|
||||
// // userInfo.value = response.value?.data;
|
||||
// // assignFields(response.value?.data);
|
||||
// }
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
activeIndex.value = path;
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
139
pages/user/password.vue
Normal file
139
pages/user/password.vue
Normal file
@@ -0,0 +1,139 @@
|
||||
<template>
|
||||
<PageBanner title="修改密码" desc="Change Password"/>
|
||||
<div class="login-layout m-auto sm:w-screen-xl w-full">
|
||||
<div class="m-auto flex sm:flex-row flex-col sm:px-0 px-3">
|
||||
<!-- 用户菜单 -->
|
||||
<UserMenu :activeIndex="activeIndex" @done="onDone" class="sm:flex hidden"/>
|
||||
<div class="flash bg-white rounded-lg w-full">
|
||||
<div class="title text-xl text-gray-700 md:px-8 p-4 md:mt-3 font-500">修改密码</div>
|
||||
<div class="sm:w-screen-md w-full sm:px-4 sm:py-2">
|
||||
<Password :form="form"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {useConfigInfo, useToken, useWebsite} from "~/composables/configState";
|
||||
import useFormData from '@/utils/use-form-data';
|
||||
import type {User} from '@/api/system/user/model';
|
||||
import {ref} from 'vue'
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult} from "~/api";
|
||||
import UserMenu from "./components/UserMenu.vue";
|
||||
import Password from './components/Password.vue';
|
||||
import type {CaptchaResult} from "~/api/passport/login/model";
|
||||
|
||||
// 配置信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const route = useRoute();
|
||||
const website = useWebsite()
|
||||
const config = useConfigInfo();
|
||||
const token = useToken();
|
||||
const userInfo = ref<User>();
|
||||
const activeIndex = ref('');
|
||||
|
||||
// 验证码 base64 数据
|
||||
const captcha = ref('');
|
||||
// 验证码内容, 实际项目去掉
|
||||
const text = ref('');
|
||||
// 图形验证码
|
||||
const imgCode = ref('');
|
||||
// 发送验证码按钮loading
|
||||
const codeLoading = ref(false);
|
||||
// 验证码倒计时时间
|
||||
const countdownTime = ref(0);
|
||||
// 验证码倒计时定时器
|
||||
let countdownTimer: number | null = null;
|
||||
|
||||
// 配置信息
|
||||
const {form, assignFields} = useFormData<User>({
|
||||
userId: undefined,
|
||||
nickname: '',
|
||||
username: '',
|
||||
phone: '',
|
||||
mobile: '',
|
||||
sex: '',
|
||||
sexName: '',
|
||||
email: '',
|
||||
oldPassword: '',
|
||||
password: '',
|
||||
password2: '',
|
||||
code: '',
|
||||
smsCode: '',
|
||||
comments: '',
|
||||
remember: true
|
||||
});
|
||||
|
||||
useHead({
|
||||
title: `用户中心 - ${config.value?.siteName}`,
|
||||
meta: [{name: website.value.keywords, content: website.value.comments}]
|
||||
});
|
||||
|
||||
|
||||
/* 发送短信验证码 */
|
||||
const sendCode = async () => {
|
||||
if (!form.phone) {
|
||||
ElMessage.error('请输入手机号码');
|
||||
return;
|
||||
}
|
||||
imgCode.value = text.value;
|
||||
codeLoading.value = true;
|
||||
|
||||
const {data: smsCode} = await useServerRequest<ApiResult<CaptchaResult>>('/sendSmsCaptcha', {
|
||||
baseURL: runtimeConfig.public.apiServer, method: "post", body: {
|
||||
phone: form.phone
|
||||
}
|
||||
});
|
||||
if (smsCode.value) {
|
||||
codeLoading.value = false;
|
||||
countdownTime.value = 30;
|
||||
// 开始对按钮进行倒计时
|
||||
countdownTimer = window.setInterval(() => {
|
||||
if (countdownTime.value <= 1) {
|
||||
countdownTimer && clearInterval(countdownTimer);
|
||||
countdownTimer = null;
|
||||
}
|
||||
countdownTime.value--;
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
const onSubmit = async () => {
|
||||
const {data: modify} = await useServerRequest<ApiResult<User>>('/auth/password', {
|
||||
baseURL: runtimeConfig.public.apiServer,
|
||||
method: 'put',
|
||||
body: form
|
||||
})
|
||||
if (modify.value?.code == 0) {
|
||||
ElMessage.success('修改成功')
|
||||
}
|
||||
}
|
||||
|
||||
const onDone = (index: string) => {
|
||||
activeIndex.value = index;
|
||||
}
|
||||
|
||||
const reload = async () => {
|
||||
// 未登录状态(是否强制登录)
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token || token == '') {
|
||||
navigateTo('/passport/login');
|
||||
return false;
|
||||
}
|
||||
const {data: response} = await useServerRequest<ApiResult<User>>('/auth/user', {baseURL: runtimeConfig.public.apiServer})
|
||||
if (response.value?.data) {
|
||||
userInfo.value = response.value?.data;
|
||||
assignFields(response.value?.data);
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
activeIndex.value = path;
|
||||
reload();
|
||||
},
|
||||
{immediate: true}
|
||||
);
|
||||
</script>
|
||||
Reference in New Issue
Block a user