forked from gxwebsoft/websoft-cms
14 changed files with 447 additions and 39 deletions
@ -0,0 +1,86 @@ |
|||||
|
<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" @click="openSpmUrl(`/form`, item,item.formId)"> |
||||
|
<el-image |
||||
|
:src="`${item.photo}`" |
||||
|
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 flex-col gap-1.5"> |
||||
|
<div class="flex-1 text-xl cursor-pointer">{{ item.name }}</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('/form', item,item.formId)"> |
||||
|
查看详情 |
||||
|
</el-button> |
||||
|
<el-button class="w-full" size="large" :icon="ElIconEdit" @click="openSpmUrl('/form', item,item.formId)">立即预约</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"; |
||||
|
import type {Form} from "~/api/cms/form/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<Form[]>([]); |
||||
|
|
||||
|
// 请求数据 |
||||
|
const reload = async () => { |
||||
|
const {data: response} = await useServerRequest<ApiResult<PageResult<Product>>>('/cms/form/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> |
@ -0,0 +1,150 @@ |
|||||
|
<template> |
||||
|
<div class="page-main md:w-screen-xl m-auto p-3" v-if="form"> |
||||
|
<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>{{ data?.name }}</span> |
||||
|
</div> |
||||
|
</template> |
||||
|
<p v-html="data?.comments"></p> |
||||
|
</el-card> |
||||
|
<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-form ref="formRef" :model="form" label-position="top" class="w-full sm:py-2" size="large" status-icon> |
||||
|
<el-form-item label="您的姓名" prop="name"> |
||||
|
<el-input |
||||
|
v-model="form.name" |
||||
|
placeholder="张三" |
||||
|
/> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="您的年龄(岁)" prop="age"> |
||||
|
<el-input-number |
||||
|
v-model="form.age" |
||||
|
placeholder="24" |
||||
|
:min="6" |
||||
|
:max="99" |
||||
|
/> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="联系电话" prop="phone"> |
||||
|
<el-input |
||||
|
v-model="form.phone" |
||||
|
:maxlength="11" |
||||
|
placeholder="13800138000" |
||||
|
/> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="您预约的疫苗" prop="age"> |
||||
|
<el-radio-group v-model="form.type"> |
||||
|
<el-radio value="九阶" size="large" border>九阶</el-radio> |
||||
|
<el-radio value="四阶" size="large" border>四阶</el-radio> |
||||
|
</el-radio-group> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="本次接种的剂次" prop="number"> |
||||
|
<el-checkbox-group v-model="checkList"> |
||||
|
<el-checkbox label="第一剂" value="第一剂" /> |
||||
|
<el-checkbox label="第二剂" value="第二剂" /> |
||||
|
<el-checkbox label="第三剂" value="第三剂" /> |
||||
|
</el-checkbox-group> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="备注信息" prop="comments"> |
||||
|
<el-input |
||||
|
v-model="form.comments" |
||||
|
:rows="5" |
||||
|
type="textarea" |
||||
|
placeholder="请输入备注信息,最多300字" |
||||
|
/> |
||||
|
</el-form-item> |
||||
|
<div class="dialog-footer w-full"> |
||||
|
<el-button type="primary" size="large" class="w-full" @click="submitForm(formRef)"> 提交 </el-button> |
||||
|
</div> |
||||
|
</el-form> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
</div> |
||||
|
</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 {FormRecord} from "~/api/cms/form-record/model"; |
||||
|
import type {Form} from "~/api/cms/form/model"; |
||||
|
|
||||
|
|
||||
|
const props = withDefaults( |
||||
|
defineProps<{ |
||||
|
title?: string; |
||||
|
data?: Form; |
||||
|
}>(), |
||||
|
{} |
||||
|
); |
||||
|
|
||||
|
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<FormRecord>({ |
||||
|
formRecordId: undefined, |
||||
|
formId: undefined, |
||||
|
name: undefined, |
||||
|
age: undefined, |
||||
|
type: undefined, |
||||
|
extra: undefined, |
||||
|
formData: undefined, |
||||
|
formObj: undefined, |
||||
|
userId: undefined, |
||||
|
phone: undefined, |
||||
|
sortNumber: undefined, |
||||
|
comments: undefined, |
||||
|
status: undefined, |
||||
|
createTime: undefined, |
||||
|
layout: undefined |
||||
|
}); |
||||
|
|
||||
|
|
||||
|
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 |
||||
|
form.formId = props.data?.formId; |
||||
|
form.extra = checkList.value.join(',') |
||||
|
useClientRequest<ApiResult<any>>(`/cms/form-record`, { |
||||
|
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) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
</script> |
@ -0,0 +1,100 @@ |
|||||
|
<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="sm:py-4 sm:px-0 mx-3 py-2"> |
||||
|
<el-breadcrumb :separator-icon="ArrowRight"> |
||||
|
<el-breadcrumb-item :to="{ path: '/' }"> |
||||
|
<el-icon class="cursor-pointer"> |
||||
|
<ElIconHouse/> |
||||
|
</el-icon> |
||||
|
</el-breadcrumb-item> |
||||
|
<el-breadcrumb-item>项目详情</el-breadcrumb-item> |
||||
|
</el-breadcrumb> |
||||
|
</div> |
||||
|
<div class="py-1 sm:py-16 px-3" _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 id="mse"></div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
import {ArrowRight} from '@element-plus/icons-vue' |
||||
|
import Player from "xgplayer"; |
||||
|
import 'xgplayer/dist/index.min.css'; |
||||
|
|
||||
|
const props = withDefaults( |
||||
|
defineProps<{ |
||||
|
title?: string; |
||||
|
desc?: string; |
||||
|
buyUrl?: string; |
||||
|
form?: any; |
||||
|
value?: 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', |
||||
|
value: 4.2 |
||||
|
} |
||||
|
); |
||||
|
|
||||
|
const url = ref<string>(''); |
||||
|
const poster = ref<string>(''); |
||||
|
|
||||
|
onMounted(() => { |
||||
|
new Player({ |
||||
|
id: "mse", //元素id |
||||
|
lang: "zh", //设置中文 |
||||
|
volume: 0, // 默认静音 |
||||
|
autoplay: false, //自动播放 |
||||
|
screenShot: false, // 开启截图功能 |
||||
|
//视频地址 |
||||
|
url: url.value, |
||||
|
//封面图 |
||||
|
poster: poster.value, |
||||
|
fluid: true, // 填满屏幕 (流式布局) |
||||
|
playbackRate: [0.5, 1, 2] //传入倍速可选数组 |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
watch( |
||||
|
() => props.form.video, |
||||
|
(video) => { |
||||
|
console.log(video,'=>video') |
||||
|
url.value = 'https://oss.wsdns.cn/20240417/9339681f3bc14999bfb2d26491f1c96e.mp4'; |
||||
|
poster.value = props.form.photo; |
||||
|
}, |
||||
|
{ immediate: true } |
||||
|
); |
||||
|
</script> |
||||
|
<style scoped lang="less"> |
||||
|
.rounded-avatar{ |
||||
|
border-radius: 30px; |
||||
|
} |
||||
|
.rounded-avatar-xs{ |
||||
|
border-radius: 20px; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,65 @@ |
|||||
|
<!-- 文章详情 --> |
||||
|
<template> |
||||
|
<PageBanner :form="form" /> |
||||
|
|
||||
|
<Comments :data="form" /> |
||||
|
</template> |
||||
|
<script setup lang="ts"> |
||||
|
import type { ApiResult } from '~/api'; |
||||
|
import { useServerRequest } from '~/composables/useServerRequest'; |
||||
|
import { useWebsite } from '~/composables/configState'; |
||||
|
import { getIdBySpm } from '~/utils/common'; |
||||
|
import useFormData from '~/utils/use-form-data'; |
||||
|
import PageBanner from './components/PageBanner.vue'; |
||||
|
import type { Form } from '~/api/cms/form/model'; |
||||
|
import Comments from "~/pages/form/components/Comments.vue"; |
||||
|
|
||||
|
// 引入状态管理 |
||||
|
const route = useRoute(); |
||||
|
const website = useWebsite(); |
||||
|
|
||||
|
// 配置信息 |
||||
|
const { form, assignFields } = useFormData<Form>({ |
||||
|
formId: undefined, |
||||
|
name: '', |
||||
|
photo: '', |
||||
|
background: '', |
||||
|
video: '', |
||||
|
layout: '', |
||||
|
comments: '', |
||||
|
status: undefined, |
||||
|
createTime: '' |
||||
|
}); |
||||
|
|
||||
|
// 请求数据 |
||||
|
const reload = async () => { |
||||
|
// 要求登录 |
||||
|
// if (!token.value || token.value == '') { |
||||
|
// openSpmUrl('/passport/login'); |
||||
|
// return; |
||||
|
// } |
||||
|
// 存在spm(优先级高) |
||||
|
const { data: item } = await useServerRequest<ApiResult<Form>>('/cms/form/' + getIdBySpm(5)); |
||||
|
if (item.value?.data) { |
||||
|
assignFields(item.value.data); |
||||
|
form.comments = item.value?.data?.comments; |
||||
|
} |
||||
|
|
||||
|
// seo |
||||
|
useHead({ |
||||
|
title: `${form.name} - ${website.value.websiteName}`, |
||||
|
bodyAttrs: { |
||||
|
class: 'page-container' |
||||
|
} |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
watch( |
||||
|
() => route.path, |
||||
|
path => { |
||||
|
console.log(path, '=>Path'); |
||||
|
reload(); |
||||
|
}, |
||||
|
{ immediate: true } |
||||
|
); |
||||
|
</script> |
Loading…
Reference in new issue