mirror of
https://github.com/ialley-workshop-open/uni-halo.git
synced 2026-09-12 16:40:40 +08:00
chore: 批量新增项目依赖、工具函数、页面与组件资源
1. 新增mp-html、qs等生产依赖,补全项目基础库 2. 新增平台判断、缓存、工具函数等通用工具集 3. 新增标签页、网站浏览页、关于页等业务页面 4. 新增分类卡片、通知弹窗等业务组件 5. 新增uts-progressNotification、liu-poster、uhalo-upgrade等uni模块 6. 补充audio/video组件样式补件,修复uni-components路径缺失问题 7. 新增环境变量Halo个人令牌配置项 8. 重构store导出结构,新增appConfig/halo/setting三个状态模块 9. 新增tsconfig编译目标配置,适配更高版本ES语法
This commit is contained in:
@@ -0,0 +1,321 @@
|
||||
<script lang="ts" setup>
|
||||
/**
|
||||
* 评论弹窗(源自旧项目 components/comment-modal,新建复刻)
|
||||
* 支持新增评论 / 回复评论,含匿名评论验证码(cookie 链路)
|
||||
*/
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { addPostComment, addPostCommentReply } from '@/api/halo'
|
||||
import { getCommentWidgetCaptcha, getCommentWidgetConfig } from '@/api/uni-halo'
|
||||
import { setCache } from '@/utils/storage'
|
||||
import { deepMerge } from '@/utils/merge'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
show: boolean
|
||||
isComment?: boolean
|
||||
title?: string
|
||||
postName: string
|
||||
}>(), {
|
||||
isComment: false,
|
||||
title: '',
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'on-close', data: { isSubmit: boolean, refresh: boolean }): void
|
||||
}>()
|
||||
|
||||
const isShow = ref(false)
|
||||
|
||||
interface ICaptchaConfig {
|
||||
security?: {
|
||||
captcha?: {
|
||||
anonymousCommentCaptcha?: boolean
|
||||
[key: string]: unknown
|
||||
}
|
||||
}
|
||||
editor?: {
|
||||
placeholder?: string
|
||||
}
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
interface ICommentForm {
|
||||
allowNotification: boolean
|
||||
author: string
|
||||
avatar: string
|
||||
authorUrl: string
|
||||
content: string
|
||||
email: string
|
||||
postName: string
|
||||
captchaCode?: string
|
||||
}
|
||||
|
||||
const config = ref<ICaptchaConfig>({
|
||||
security: {
|
||||
captcha: {
|
||||
anonymousCommentCaptcha: false,
|
||||
},
|
||||
},
|
||||
editor: {
|
||||
placeholder: '请输入内容,不超过200字符...',
|
||||
},
|
||||
})
|
||||
|
||||
const captchaData = ref<{ image: string, status: 'loading' | 'success' | 'fail' }>({
|
||||
image: '',
|
||||
status: 'loading',
|
||||
})
|
||||
|
||||
const form = ref<ICommentForm>({
|
||||
allowNotification: true,
|
||||
author: '',
|
||||
avatar: '',
|
||||
authorUrl: '',
|
||||
content: '',
|
||||
email: '',
|
||||
postName: props.postName,
|
||||
captchaCode: undefined,
|
||||
})
|
||||
|
||||
const calcTitle = computed(() => {
|
||||
if (props.isComment)
|
||||
return props.title || '新增评论'
|
||||
return `回复用户:${props.title}`
|
||||
})
|
||||
|
||||
function handleResetForm() {
|
||||
form.value = {
|
||||
allowNotification: true,
|
||||
author: '',
|
||||
avatar: '',
|
||||
authorUrl: '',
|
||||
content: '',
|
||||
email: '',
|
||||
postName: props.postName,
|
||||
captchaCode: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取评论组件配置(验证码开关) */
|
||||
async function handleGetConfig() {
|
||||
try {
|
||||
const res = await getCommentWidgetConfig()
|
||||
config.value = deepMerge(config.value as Record<string, unknown>, res.data as Record<string, unknown>) as ICaptchaConfig
|
||||
if (config.value?.security?.captcha?.anonymousCommentCaptcha) {
|
||||
handleGetCaptchaImage()
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
console.error('获取验证码配置失败', err)
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取评论验证码图片 */
|
||||
async function handleGetCaptchaImage() {
|
||||
captchaData.value.status = 'loading'
|
||||
try {
|
||||
const res = await getCommentWidgetCaptcha()
|
||||
form.value.captchaCode = undefined
|
||||
captchaData.value.image = res.data as unknown as string
|
||||
captchaData.value.status = 'success'
|
||||
}
|
||||
catch (err) {
|
||||
console.error('获取验证码失败', err)
|
||||
captchaData.value.status = 'fail'
|
||||
captchaData.value.image = ''
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化访客信息 */
|
||||
function handleInitVisitor() {
|
||||
const visitor = uni.getStorageSync('Visitor')
|
||||
if (!visitor)
|
||||
return
|
||||
try {
|
||||
const v = JSON.parse(visitor)
|
||||
form.value.author = v.author || ''
|
||||
form.value.avatar = v.avatar || ''
|
||||
form.value.email = v.email || ''
|
||||
form.value.authorUrl = v.authorUrl || ''
|
||||
}
|
||||
catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
/** 保存访客信息 */
|
||||
function handleSetVisitor() {
|
||||
setCache('Visitor', {
|
||||
author: form.value.author,
|
||||
avatar: form.value.avatar,
|
||||
email: form.value.email,
|
||||
authorUrl: form.value.authorUrl,
|
||||
})
|
||||
}
|
||||
|
||||
/** 提交校验 */
|
||||
function validateForm(): boolean {
|
||||
if (!form.value.content.trim()) {
|
||||
uni.showToast({ icon: 'none', title: '请填写评论内容' })
|
||||
return false
|
||||
}
|
||||
if (!form.value.author.trim()) {
|
||||
uni.showToast({ icon: 'none', title: '请填写昵称' })
|
||||
return false
|
||||
}
|
||||
if (!form.value.email.trim()) {
|
||||
uni.showToast({ icon: 'none', title: '请填写邮箱' })
|
||||
return false
|
||||
}
|
||||
if (config.value?.security?.captcha?.anonymousCommentCaptcha && !form.value.captchaCode?.trim()) {
|
||||
uni.showToast({ icon: 'none', title: '请填写验证码结果!' })
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
async function handleHandle() {
|
||||
if (!validateForm())
|
||||
return
|
||||
|
||||
uni.showLoading({ title: '正在提交...' })
|
||||
|
||||
try {
|
||||
if (props.isComment) {
|
||||
// 新增评论
|
||||
await addPostComment({
|
||||
raw: form.value.content,
|
||||
content: form.value.content,
|
||||
owner: {
|
||||
displayName: form.value.author,
|
||||
email: form.value.email,
|
||||
website: form.value.authorUrl,
|
||||
},
|
||||
subjectRef: {
|
||||
group: 'content.halo.run',
|
||||
kind: 'Post',
|
||||
name: form.value.postName,
|
||||
version: 'v1alpha1',
|
||||
},
|
||||
captchaCode: config.value?.security?.captcha?.anonymousCommentCaptcha ? form.value.captchaCode : undefined,
|
||||
})
|
||||
uni.showToast({ icon: 'none', title: '评论成功,可能需要审核!' })
|
||||
}
|
||||
else {
|
||||
// 回复评论
|
||||
await addPostCommentReply(form.value.postName, {
|
||||
raw: form.value.content,
|
||||
content: form.value.content,
|
||||
owner: {
|
||||
displayName: form.value.author,
|
||||
email: form.value.email,
|
||||
website: form.value.authorUrl,
|
||||
},
|
||||
captchaCode: config.value?.security?.captcha?.anonymousCommentCaptcha ? form.value.captchaCode : undefined,
|
||||
})
|
||||
uni.showToast({ icon: 'none', title: '回复成功,可能需要审核!' })
|
||||
}
|
||||
|
||||
handleSetVisitor()
|
||||
handleClose(true)
|
||||
handleResetForm()
|
||||
}
|
||||
catch (err) {
|
||||
console.error('评论提交失败', err)
|
||||
if (config.value?.security?.captcha?.anonymousCommentCaptcha) {
|
||||
captchaData.value.status = 'success'
|
||||
form.value.captchaCode = undefined
|
||||
handleGetCaptchaImage()
|
||||
}
|
||||
uni.showToast({ icon: 'none', title: '提交失败' })
|
||||
}
|
||||
finally {
|
||||
uni.hideLoading()
|
||||
}
|
||||
}
|
||||
|
||||
function handleOnChange(isOpen: boolean) {
|
||||
isShow.value = isOpen
|
||||
if (!isOpen) {
|
||||
emit('on-close', { isSubmit: false, refresh: false })
|
||||
}
|
||||
}
|
||||
|
||||
function handleClose(refresh = false) {
|
||||
isShow.value = false
|
||||
emit('on-close', { isSubmit: true, refresh })
|
||||
}
|
||||
|
||||
watch(() => props.show, (newVal) => {
|
||||
if (!newVal)
|
||||
return
|
||||
isShow.value = true
|
||||
handleResetForm()
|
||||
form.value.postName = props.postName
|
||||
handleGetConfig()
|
||||
handleInitVisitor()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<wd-popup v-model="isShow" position="bottom" custom-style="border-radius:12rpx 12rpx 0 0;" @close="handleOnChange(false)">
|
||||
<view class="uh-comment-modal box-border max-h-[71vh] overflow-y-auto p-3">
|
||||
<view class="title my-6 text-center text-[32rpx] font-bold">
|
||||
{{ calcTitle }}
|
||||
</view>
|
||||
|
||||
<view class="form">
|
||||
<view class="form-item mb-5 flex items-center">
|
||||
<textarea
|
||||
v-model="form.content"
|
||||
class="content-input box-border w-full rounded-xl bg-[#f5f5f5] p-5 text-[26rpx]"
|
||||
:placeholder="config.editor?.placeholder || '请输入内容,不超过200字符...'"
|
||||
:maxlength="200"
|
||||
style="height: 200rpx;"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="form-item mb-5 flex items-center">
|
||||
<text class="label w-[140rpx] shrink-0 text-[26rpx] text-[#666]">我的昵称</text>
|
||||
<input v-model="form.author" class="input h-[72rpx] flex-1 rounded-xl bg-[#f5f5f5] px-5 text-[26rpx]" placeholder="请输入您的昵称...">
|
||||
</view>
|
||||
|
||||
<view class="form-item mb-5 flex items-center">
|
||||
<text class="label w-[140rpx] shrink-0 text-[26rpx] text-[#666]">我的邮箱</text>
|
||||
<input v-model="form.email" class="input h-[72rpx] flex-1 rounded-xl bg-[#f5f5f5] px-5 text-[26rpx]" placeholder="请输入您的邮箱...">
|
||||
</view>
|
||||
|
||||
<view class="form-item mb-5 flex items-center">
|
||||
<text class="label w-[140rpx] shrink-0 text-[26rpx] text-[#666]">我的网站</text>
|
||||
<input v-model="form.authorUrl" class="input h-[72rpx] flex-1 rounded-xl bg-[#f5f5f5] px-5 text-[26rpx]" placeholder="请输入您的网址...">
|
||||
</view>
|
||||
|
||||
<!-- 匿名评论验证码 -->
|
||||
<view v-if="config?.security?.captcha?.anonymousCommentCaptcha" class="form-item mb-5 flex items-center">
|
||||
<text class="label w-[140rpx] shrink-0 text-[26rpx] text-[#666]">验证码</text>
|
||||
<view class="captcha-row flex flex-1 items-center gap-4">
|
||||
<input v-model="form.captchaCode" class="input h-[72rpx] flex-1 rounded-xl bg-[#f5f5f5] px-5 text-[26rpx]" placeholder="请输入验证码结果">
|
||||
<view class="captcha-wrapper h-[68rpx] w-[200rpx] flex shrink-0 items-center justify-center">
|
||||
<text v-if="captchaData.status === 'loading'" class="captcha-tip text-[24rpx] text-[#999]">获取中...</text>
|
||||
<text v-else-if="captchaData.status === 'fail'" class="captcha-tip text-[24rpx] text-[#f56c6c]" @click="handleGetCaptchaImage()">请重试</text>
|
||||
<image v-else :src="captchaData.image" class="captcha-img h-full w-full" mode="aspectFit" @click="handleGetCaptchaImage()" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="submit-btn my-6">
|
||||
<wd-button type="primary" block size="medium" @click="handleHandle">
|
||||
提交
|
||||
</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</wd-popup>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.uh-comment-modal {
|
||||
.content-input {
|
||||
min-height: 200rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user