feat: 增加腾讯AI

share-code
xjs 2026-05-28 10:17:05 +08:00
parent 021f65a7f9
commit a4e0d74342
113 changed files with 6553 additions and 398 deletions

4
env/.env vendored
View File

@ -2,7 +2,9 @@ VITE_APP_TITLE = '六纬中考通'
VITE_APP_PORT = 9000
VITE_UNI_APPID = 'H57F2ACE4'
VITE_WX_APPID = 'wx4b925e36c17dd54a'
# VITE_WX_APPID = 'wxc48ad15d58a3e417' 六纬中考通
# VITE_WX_APPID = 'wx4b925e36c17dd54a' 六纬裂变
VITE_WX_APPID = 'wxc48ad15d58a3e417'
# h5部署网站的base配置到 manifest.config.ts 里的 h5.router.base
# https://uniapp.dcloud.net.cn/collocation/manifest.html#h5-router

View File

@ -8,6 +8,10 @@ export default defineUniPages({
navigationBarBackgroundColor: '#FFFFFF',
navigationBarTextStyle: 'black',
backgroundColor: '#FFFFFF',
// 全局注册微信原生组件(仅 mp-weixin 编译器会读取此字段)
usingComponents: {
'markdown-preview': '/wxcomponents/agent-ui/wd-markdown/index',
},
},
easycom: {
autoscan: true,

292
src/pages-ai/ai/index.vue Normal file
View File

@ -0,0 +1,292 @@
<script lang="ts" setup>
import type { AgentConfig, ModelConfig } from '../components/agent-config'
import type { AiMessage } from '@/store/ai'
import { useAiStore } from '@/store'
import {
defaultAgentConfig,
defaultModelConfig,
generateMessageId,
} from '../components/agent-config'
import { fetchRecommendQuestions, streamMessage } from '../components/agent-service'
import AgentInput from '../components/AgentInput.vue'
import AgentMessage from '../components/AgentMessage.vue'
definePage({
style: {
navigationBarTitleText: 'AI 助手',
},
})
const agentConfig = reactive<AgentConfig>({ ...defaultAgentConfig })
// model 使 bot
const _modelConfig = reactive<ModelConfig>({ ...defaultModelConfig })
const aiStore = useAiStore()
const paging = ref<any>(null)
const messages = ref<AiMessage[]>([])
const loading = ref(false)
/** 当前轮回复结束后由 getRecommendQuestions 返回的追问建议 */
const recommendQuestions = ref<string[]>([])
// controller cancelled
let cancelFlag = false
onLoad(() => {
// #ifdef MP-WEIXIN
if (typeof wx !== 'undefined' && wx.cloud) {
wx.cloud.init({
env: agentConfig.cloudEnvId,
traceUser: true,
})
}
// #endif
aiStore.ensureThreadId()
})
// z-paging store
// 0 =
function queryList() {
if (aiStore.messages.length === 0) {
aiStore.addMessage({
id: generateMessageId(),
role: 'assistant',
content: agentConfig.welcomeMsg,
createdAt: Date.now(),
})
}
paging.value?.complete(aiStore.messages.slice().reverse())
}
async function handleSend(text: string) {
if (loading.value) {
return
}
cancelFlag = false
loading.value = true
//
recommendQuestions.value = []
const userMsg: AiMessage = {
id: generateMessageId(),
role: 'user',
content: text,
createdAt: Date.now(),
}
const aiMsg: AiMessage = {
id: generateMessageId(),
role: 'assistant',
content: '',
pending: true,
// +1 userMsg
createdAt: Date.now() + 1,
}
// store z-paging
// queryList user ai
// z-paging addChatRecordData +
// totalData = [aiMsg, userMsg, ...] 0 = = aiMsg
// userMsg aiMsg " AI "
aiStore.addMessage(userMsg)
aiStore.addMessage(aiMsg)
paging.value?.addChatRecordData([userMsg, aiMsg])
try {
await streamMessage({
botId: agentConfig.botId,
threadId: aiStore.ensureThreadId(),
// aiMsg
history: aiStore.messages.slice(0, -1),
prompt: text,
onDelta: (delta) => {
if (cancelFlag) {
return
}
aiStore.appendDelta(aiMsg.id, delta)
},
onError: (msg) => {
aiStore.updateMessage(aiMsg.id, {
error: true,
pending: false,
content: msg,
})
},
})
aiStore.updateMessage(aiMsg.id, { pending: false })
//
if (!cancelFlag) {
void loadRecommendQuestions(text)
}
}
catch (err) {
aiStore.updateMessage(aiMsg.id, {
pending: false,
error: true,
content: aiStore.messages.find(m => m.id === aiMsg.id)?.content
|| (err instanceof Error ? err.message : '生成失败'),
})
}
finally {
loading.value = false
}
}
function handleStop() {
cancelFlag = true
loading.value = false
// pending
const pending = aiStore.messages.find(m => m.pending)
if (pending) {
aiStore.updateMessage(pending.id, { pending: false })
}
}
function handleRetry(id: string) {
const failedIdx = aiStore.messages.findIndex(m => m.id === id)
if (failedIdx <= 0) {
return
}
// ai
const userMsg = aiStore.messages[failedIdx - 1]
if (!userMsg || userMsg.role !== 'user') {
return
}
//
aiStore.removeMessage(id)
handleSend(userMsg.content)
}
function handleClickQuestion(question: string) {
recommendQuestions.value = []
handleSend(question)
}
async function loadRecommendQuestions(prompt: string) {
// user / assistant
const last = aiStore.messages.slice(-2).map(m => ({
role: m.role === 'user' ? ('user' as const) : ('assistant' as const),
content: m.content,
}))
try {
const questions = await fetchRecommendQuestions({
botId: agentConfig.botId,
lastPair: last,
prompt,
max: 3,
onProgress: (qs) => {
recommendQuestions.value = qs
},
})
recommendQuestions.value = questions
}
catch (error) {
console.error('[recommend]', error)
recommendQuestions.value = []
}
}
function handleClear() {
aiStore.clear()
recommendQuestions.value = []
paging.value?.reload()
}
</script>
<template>
<view class="h-screen w-full flex flex-col bg-[#F4F6FA]">
<!-- 顶部清空按钮 -->
<view class="flex items-center justify-between bg-white px-[24rpx] py-[20rpx] shadow-[0_2rpx_8rpx_rgba(0,0,0,0.03)]">
<view class="text-[30rpx] text-[#1F2329] font-500">
{{ agentConfig.botName }}
</view>
<view
class="rounded-[24rpx] bg-[#F5F6F8] px-[20rpx] py-[8rpx] text-[24rpx] text-[#666] active:opacity-60"
@click="handleClear"
>
清空对话
</view>
</view>
<!-- 消息列表聊天模式 -->
<view class="min-h-0 flex-1">
<z-paging
ref="paging"
v-model="messages"
:default-page-size="50"
:auto-show-system-loading="false"
:loading-more-enabled="false"
:show-refresher-when-reload="false"
:show-loading-more-no-more-view="false"
use-chat-record-mode
auto-adjust-position-when-chat
auto-to-bottom-when-chat
:paging-style="{ backgroundColor: '#F4F6FA' }"
@query="queryList"
>
<!-- 推荐问题 -->
<template v-if="messages.length <= 1 && agentConfig.initQuestions.length" #top>
<view class="flex flex-col gap-[16rpx] px-[24rpx] py-[20rpx]">
<view class="text-[24rpx] text-[#999] leading-[1.4]">
你可以试试这样问
</view>
<view class="flex flex-wrap gap-[16rpx]">
<view
v-for="q in agentConfig.initQuestions"
:key="q"
class="border-1 border-[#1580FF] rounded-[24rpx] border-solid bg-white px-[20rpx] py-[10rpx] text-[24rpx] text-[#1580FF] active:opacity-60"
@click="handleClickQuestion(q)"
>
{{ q }}
</view>
</view>
</view>
</template>
<template #default>
<agent-message
v-for="item in messages"
:key="item.id"
:message="item"
:bot-name="agentConfig.botName"
@retry="handleRetry"
/>
</template>
<template #bottom>
<!-- 一轮对话结束后的追问建议 -->
<view
v-if="recommendQuestions.length"
class="flex flex-wrap gap-[12rpx] bg-[#F4F6FA] px-[24rpx] pb-[16rpx] pt-[8rpx]"
>
<view class="w-full text-[24rpx] text-[#999] leading-[1.4]">
你可能还想问
</view>
<view
v-for="q in recommendQuestions"
:key="q"
class="border-1 border-[#1580FF] rounded-[24rpx] border-solid bg-white px-[20rpx] py-[10rpx] text-[24rpx] text-[#1580FF] active:opacity-60"
@click="handleClickQuestion(q)"
>
{{ q }}
</view>
</view>
<agent-input
:placeholder="agentConfig.placeholder"
:loading="loading"
@send="handleSend"
@stop="handleStop"
@clear="handleClear"
/>
</template>
</z-paging>
</view>
</view>
</template>
<style lang="scss" scoped>
//
</style>

View File

@ -0,0 +1,112 @@
<script lang="ts" setup>
import { ref } from 'vue'
const props = defineProps<{
placeholder?: string
loading?: boolean
}>()
const emit = defineEmits<{
(event: 'send', text: string): void
(event: 'stop'): void
(event: 'clear'): void
}>()
const inputValue = ref('')
const showActions = ref(false)
function handleSend() {
const text = inputValue.value.trim()
if (!text || props.loading) {
return
}
emit('send', text)
inputValue.value = ''
}
function toggleActions() {
showActions.value = !showActions.value
}
function handleClickClear() {
showActions.value = false
uni.showModal({
title: '清空对话',
content: '将删除所有对话记录,确认继续?',
confirmColor: '#EB5241',
success: (res) => {
if (res.confirm) {
emit('clear')
}
},
})
}
</script>
<template>
<view class="flex flex-col bg-white pb-safe">
<!-- 输入区 -->
<view class="flex items-center gap-[16rpx] px-[24rpx] py-[18rpx]">
<view class="min-h-[72rpx] flex flex-1 items-center rounded-[40rpx] bg-[#F5F6F8] px-[24rpx] py-[12rpx]">
<textarea
v-model="inputValue"
class="max-h-[200rpx] w-full bg-transparent text-[28rpx] text-[#1F2329]"
:placeholder="props.placeholder || '输入你的问题'"
placeholder-style="color:#A6AFBD;font-size:28rpx;"
auto-height
:show-confirm-bar="false"
:adjust-position="true"
cursor-spacing="20"
confirm-type="send"
@confirm="handleSend"
@focus="showActions = false"
/>
</view>
<!-- 发送 / 停止 / 展开更多 -->
<view
v-if="!props.loading && !inputValue.trim()"
class="h-[72rpx] w-[72rpx] flex items-center justify-center rounded-full bg-[#F5F6F8] text-[40rpx] text-[#666] transition active:opacity-60"
:class="showActions ? 'rotate-45' : ''"
@click="toggleActions"
>
+
</view>
<view
v-else-if="!props.loading"
class="h-[72rpx] w-[120rpx] flex items-center justify-center rounded-[36rpx] bg-[#1580FF] text-[28rpx] text-white font-500 transition active:opacity-80"
@click="handleSend"
>
发送
</view>
<view
v-else
class="h-[72rpx] w-[120rpx] flex items-center justify-center border-1 border-[#1580FF] rounded-[36rpx] border-solid bg-white text-[28rpx] text-[#1580FF] font-500 active:opacity-80"
@click="emit('stop')"
>
停止
</view>
</view>
<!-- 展开的操作面板 -->
<view
v-if="showActions"
class="grid grid-cols-4 gap-[16rpx] px-[24rpx] pb-[24rpx] pt-[12rpx]"
>
<view
class="flex flex-col items-center gap-[8rpx] rounded-[16rpx] bg-[#F5F6F8] py-[24rpx] active:opacity-60"
@click="handleClickClear"
>
<view class="h-[64rpx] w-[64rpx] flex items-center justify-center rounded-full bg-[#FFE4E1] text-[32rpx] text-[#EB5241]">
</view>
</view>
</view>
</view>
</template>
<style lang="scss" scoped>
.rotate-45 {
transform: rotate(45deg);
}
</style>

View File

@ -0,0 +1,97 @@
<script lang="ts" setup>
import type { AiMessage } from '@/store/ai'
const props = defineProps<{
message: AiMessage
botName?: string
}>()
defineEmits<{
(event: 'retry', id: string): void
}>()
</script>
<template>
<view
class="cell-flip flex px-[24rpx] py-[16rpx]"
:class="props.message.role === 'user' ? 'justify-end' : 'justify-start'"
>
<!-- AI 头像 -->
<view
v-if="props.message.role !== 'user'"
class="mr-[16rpx] h-[64rpx] w-[64rpx] flex shrink-0 items-center justify-center rounded-full bg-[#1580FF] text-[24rpx] text-white"
>
AI
</view>
<view class="max-w-[78%] flex flex-col gap-[8rpx]">
<view
class="break-all rounded-[16rpx] px-[24rpx] py-[18rpx] text-[28rpx] leading-[1.6]"
:class="props.message.role === 'user'
? 'bg-[#1580FF] text-white rounded-tr-[4rpx]'
: 'rounded-tl-[4rpx] bg-white text-[#1F2329] shadow-[0_2rpx_12rpx_rgba(0,0,0,0.04)]'"
>
<!-- AI 回复用 markdown 渲染仅微信小程序 -->
<!-- #ifdef MP-WEIXIN -->
<markdown-preview
v-if="props.message.role !== 'user'"
:markdown="props.message.content || (props.message.pending ? '' : '')"
:font-size="28"
/>
<text v-else class="select-text">
{{ props.message.content }}
</text>
<!-- #endif -->
<!-- 其它端 fallback -->
<!-- #ifndef MP-WEIXIN -->
<text class="select-text">
{{ props.message.content || (props.message.pending ? '...' : '') }}
</text>
<!-- #endif -->
<text v-if="props.message.pending" class="ml-[6rpx] animate-pulse">
</text>
</view>
<view
v-if="props.message.error"
class="self-start text-[24rpx] text-[#EB5241] leading-[1.4] active:opacity-60"
@click="$emit('retry', props.message.id)"
>
生成失败点击重试
</view>
</view>
<!-- 用户头像 -->
<view
v-if="props.message.role === 'user'"
class="ml-[16rpx] h-[64rpx] w-[64rpx] flex shrink-0 items-center justify-center rounded-full bg-[#FFA94D] text-[28rpx] text-white"
>
U
</view>
</view>
</template>
<style lang="scss" scoped>
/* z-paging 聊天模式整体 scaleY(-1)cell 必须再反转一次回正 */
.cell-flip {
transform: scaleY(-1);
}
.animate-pulse {
animation: pulse 1.4s ease-in-out infinite;
}
@keyframes pulse {
0%,
80%,
100% {
opacity: 0.2;
}
40% {
opacity: 1;
}
}
</style>

View File

@ -0,0 +1,50 @@
// AI Agent 相关运行时配置(页面、组件共享)
export interface AgentConfig {
/** 微信云开发环境 ID用于 wx.cloud.init */
cloudEnvId: string
/** Agent ID对应 wx.cloud.extend.AI.bot.sendMessage 的 botId */
botId: string
/** 首屏欢迎语 */
welcomeMsg: string
/** 输入框 placeholder */
placeholder: string
/** 显示在 AI 头像位置的图标 / 文字 */
botName: string
/** 默认推荐问题,点击后直接发送 */
initQuestions: string[]
}
export interface ModelConfig {
/** 模型 provider如 'cloudbase' / 'youtu' / 'hunyuan' */
modelProvider: string
/** 具体的模型 ID */
quickResponseModel: string
/** logo 图,可选 */
logo: string
}
export const defaultAgentConfig: AgentConfig = {
cloudEnvId: 'cloud1-d3g5q6bq61a240786',
botId: 'agent-wxai-4gl75um61026324f',
welcomeMsg: '你好,我是 AI 助手,有什么可以帮你?',
placeholder: '输入你的问题',
botName: 'AI 助手',
initQuestions: [
'帮我介绍一下志愿填报流程',
'中考分数线怎么查?',
'推荐几所适合的高中',
],
}
export const defaultModelConfig: ModelConfig = {
modelProvider: 'youtu-intent-pro',
quickResponseModel: '',
logo: '',
}
export function generateMessageId() {
const timestamp = Date.now().toString().slice(-8)
const random = Math.floor(Math.random() * 10000).toString().padStart(4, '0')
return `${timestamp}${random}`
}

View File

@ -0,0 +1,142 @@
// 与 wx.cloud.extend.AI.bot 通讯的服务层
// 把流式接收逻辑收敛到一个 streamMessage 函数UI 只负责消费 onDelta
import type { AiMessage } from '@/store/ai'
import { generateMessageId } from './agent-config'
interface BotEventData {
type: string
delta?: string
message?: string
[key: string]: unknown
}
export interface SendMessageOptions {
botId: string
threadId: string
/** 历史消息(最近若干轮),按 wx.cloud.extend.AI.bot 协议传 */
history: AiMessage[]
/** 当前用户输入 */
prompt: string
/** 流式增量回调 */
onDelta: (delta: string) => void
/** 错误回调,会拿到服务端返回的 message 字段 */
onError?: (message: string) => void
}
export async function streamMessage(options: SendMessageOptions): Promise<string> {
const { botId, threadId, history, prompt, onDelta, onError } = options
// wx.cloud.extend.AI.bot 协议要求传入完整 messages 列表
// 这里把 store 里的历史拼到一起,最后追加当前用户消息
const messages = [
...history
.filter(m => !m.error && (m.role === 'user' || m.role === 'assistant'))
.map(m => ({
id: m.id,
role: m.role,
content: m.content,
})),
{
id: generateMessageId(),
role: 'user',
content: prompt,
},
]
// #ifdef MP-WEIXIN
// @ts-expect-error wx.cloud.extend 由微信小程序基础库注入
const res = await wx.cloud.extend.AI.bot.sendMessage({
data: {
botId,
threadId,
runId: `run_id_${generateMessageId()}`,
messages,
tools: [],
context: [],
state: {},
forwardedProps: {},
},
})
let response = ''
for await (const event of res.eventStream) {
let data: BotEventData
try {
data = JSON.parse(event.data) as BotEventData
}
catch {
continue
}
switch (data.type) {
case 'TEXT_MESSAGE_CONTENT':
if (data.delta) {
response += data.delta
onDelta(data.delta)
}
break
case 'RUN_ERROR':
onError?.(data.message || '请求出错,请稍后再试')
throw new Error(data.message || 'RUN_ERROR')
case 'RUN_FINISHED':
break
}
}
return response
// #endif
// #ifndef MP-WEIXIN
throw new Error('AI 助手仅在微信小程序内可用')
// #endif
}
interface RecommendOptions {
botId: string
/** 最近一对 user/assistant 消息,用于上下文 */
lastPair: { role: 'user' | 'assistant', content: string }[]
/** 触发追问的用户原问题 */
prompt: string
/** 最多返回多少条 */
max?: number
/** 流式返回时增量更新(用来做"边收边渲染"),可选 */
onProgress?: (questions: string[]) => void
}
/**
* getRecommendQuestions
* agent "智能体追问"
*/
export async function fetchRecommendQuestions(options: RecommendOptions): Promise<string[]> {
const { botId, lastPair, prompt, max = 3, onProgress } = options
// #ifdef MP-WEIXIN
// @ts-expect-error wx.cloud.extend 由微信小程序基础库注入
const res = await wx.cloud.extend.AI.bot.getRecommendQuestions({
data: {
botId,
history: lastPair.map(item => ({
role: item.role,
content: item.content,
})),
msg: prompt,
agentSetting: '',
introduction: '',
name: '',
},
})
let buffer = ''
for await (const chunk of res.textStream) {
buffer += chunk
const questions = buffer.split('\n').map(s => s.trim()).filter(Boolean).slice(0, max)
onProgress?.(questions)
}
return buffer.split('\n').map(s => s.trim()).filter(Boolean).slice(0, max)
// #endif
// #ifndef MP-WEIXIN
return []
// #endif
}

View File

@ -1,14 +1,13 @@
<script lang="ts" setup>
import { useLogin } from '@/pages-fg/hooks/useUserInfo'
import {
getSessionKey,
getWxUserInfo,
setWxInfo,
} from '@/service/'
import { useTokenStore, useUserStore } from '@/store'
import Checkbox from '../components/check-group/Checkbox.vue'
import CheckboxGroup from '../components/check-group/CheckboxGroup.vue'
import { useLogin } from '@/pages-fg/hooks/useUserInfo'
import { useTokenStore, useUserStore } from '@/store'
const checked = ref([]) //
const getPhoneInfo = ref(null)
@ -16,8 +15,8 @@ const getPhoneInfo = ref(null)
const tokenStore = useTokenStore()
const userStore = useUserStore()
const getPhoneNumber = async (e: any) => {
if (e.detail.errMsg == 'getPhoneNumber:ok') {
async function getPhoneNumber(e: any) {
if (e.detail.errMsg === 'getPhoneNumber:ok') {
const detail = e.detail
const _getPhoneInfo = {
iv: detail.iv,
@ -26,12 +25,14 @@ const getPhoneNumber = async (e: any) => {
}
getPhoneInfo.value = _getPhoneInfo
await getUserInfo(detail.code)
} else if (e.detail.errMsg == 'getPhoneNumber:fail not login') {
}
else if (e.detail.errMsg === 'getPhoneNumber:fail not login') {
uni.showToast({
title: '请先登录',
icon: 'none',
})
} else {
}
else {
uni.showToast({
title: '获取手机号失败',
icon: 'none',
@ -39,7 +40,7 @@ const getPhoneNumber = async (e: any) => {
}
}
const handleClick = () => {
function handleClick() {
if (!checked.value) {
uni.showToast({
title: '您需先同意《服务条款》和《隐私条款》',
@ -60,49 +61,46 @@ const handleClick = () => {
}
//
const handleAuthReady = () => {
function handleAuthReady() {
uni.navigateBack()
}
const handleClickUserAgreement = () => {
function handleClickUserAgreement() {
uni.navigateTo({
url: '/pages-fg/login/userAgreement',
})
}
const handleClickPrivacyPolicy = () => {
function handleClickPrivacyPolicy() {
uni.navigateTo({
url: '/pages-fg/login/privacyPolicy',
})
}
async function getUserInfo(_code: string) {
const userInfo = (await useLogin()) as { code: string, errMsg: string }
const getUserInfo = async (_code: string) => {
const userInfo = (await useLogin()) as { code: string; errMsg: string }
if (userInfo.errMsg == 'login:ok') {
if (userInfo.errMsg === 'login:ok') {
const resp = await getSessionKey({ query: { JsCode: userInfo.code } })
if(resp.code !== 200){
uni.showModal({title:'登陆失败',content:resp.message})
uni.navigateBack();
if (resp.code !== 200) {
uni.showModal({ title: '登陆失败', content: resp.message })
uni.navigateBack()
}
tokenStore.setTokenInfo({ token: resp.result.accessToken, expiresIn: 7 * 24 * 60 * 60 })
userStore.setUserOpenId(resp.result.openId)
setWxInfo({options:{query:{code:_code, openId:resp.result.openId}}})
getWxUserInfo().then(resp => {
if(resp.code == 200){
setWxInfo({ options: { query: { code: _code, openId: resp.result.openId } } })
getWxUserInfo().then((resp) => {
if (resp.code === 200) {
userStore.setUserExtend(resp.result.userExtend)
userStore.setUserInfo({nickName:resp.result.nickName,mobile:resp.result.mobile})
userStore.setUserInfo({ nickName: resp.result.nickName, mobile: resp.result.mobile })
userStore.setSex(resp.result.sex)
userStore.setUserAvatar(resp.result.avatar)
}
})
uni.navigateBack()
} else {
}
else {
uni.showToast({
title: '您需先授权',
icon: 'none',
@ -113,35 +111,36 @@ const getUserInfo = async (_code: string) => {
<template>
<view class="h-screen flex flex-col bg-white">
<view class="flex flex-col justify-center items-center flex-1 pb-safe mt-[-100px]">
<view class="w-[424rpx] h-[424rpx]">
<image class="w-[424rpx] h-[424rpx]" src="https://lwzk.ycymedu.com/img/home/logo.png" mode="aspectFit">
</image>
<view class="mt-[-100px] flex flex-1 flex-col items-center justify-center pb-safe">
<view class="h-[424rpx] w-[424rpx]">
<image class="h-[424rpx] w-[424rpx]" src="https://lwzk.ycymedu.com/img/home/logo.png" mode="aspectFit" />
</view>
<!-- #ifdef MP-WEIXIN -->
<button
class="w-[493rpx]! mb-[40rpx] h-[88rpx]! rounded-[44rpx] text-[32rpx] text-white flex items-center justify-center "
:class="checked.length > 0 ? 'bg-[#1580FF]' : 'bg-[#BFBFBF]'" @click.stop="handleClick"
open-type="getPhoneNumber" @getphonenumber="getPhoneNumber" :disabled="checked.length === 0">
class="mb-[40rpx] flex items-center justify-center rounded-[44rpx] text-[32rpx] text-white h-[88rpx]! w-[493rpx]!"
:class="checked.length > 0 ? 'bg-[#1580FF]' : 'bg-[#BFBFBF]'" open-type="getPhoneNumber"
:disabled="checked.length === 0" @click.stop="handleClick" @getphonenumber="getPhoneNumber"
>
一键登录
</button>
<!-- #endif -->
<!-- #ifdef MP-ALIPAY -->
<button
class="w-[493rpx]! mb-[40rpx] h-[88rpx]! rounded-[44rpx] text-[32rpx] text-white flex items-center justify-center "
:class="checked.length > 0 ? 'bg-[#1580FF]' : 'bg-[#BFBFBF]'" @click.stop="handleClick">
class="mb-[40rpx] flex items-center justify-center rounded-[44rpx] text-[32rpx] text-white h-[88rpx]! w-[493rpx]!"
:class="checked.length > 0 ? 'bg-[#1580FF]' : 'bg-[#BFBFBF]'" @click.stop="handleClick"
>
一键登录
</button>
<!-- #endif -->
<view class="flex items-center flex-nowrap">
<view class="flex flex-nowrap items-center">
<CheckboxGroup v-model="checked" class="check-class mr-[10rpx]">
<Checkbox name="1" cell shape="button" class="custom-checkbox"></Checkbox>
<Checkbox name="1" cell shape="button" class="custom-checkbox" />
</CheckboxGroup>
<view class="flex items-center">
<text class="text-[24rpx] whitespace-nowrap">
<text class="whitespace-nowrap text-[24rpx]">
已阅读并同意
<text class="text-[#1580FF]" @click.stop="handleClickUserAgreement">
<text>用户协议</text>

View File

@ -1,53 +1,50 @@
<script lang="ts" setup>
import { useUserStore,useTokenStore } from '@/store'
import { getAssistant } from '@/service'
import { useTokenStore, useUserStore } from '@/store'
definePage({
style: {
navigationBarTitleText: '六纬AI小助手',
},
excludeLoginPath: true,
style: {
navigationBarTitleText: '六纬AI小助手',
},
excludeLoginPath: true,
})
const userStore = useUserStore()
const tokenStore = useTokenStore()
//chat.ycymedu.com
//chatv2.ycymedu.com
// chat.ycymedu.com
// chatv2.ycymedu.com
const url = ref(``)
const handleChildMessage = (event) => {
console.log('子应用传递的消息', event)
function handleChildMessage(event) {
console.log('子应用传递的消息', event)
}
onLoad((options) => {
getAssistant().then((res) => {
if (res.code === 200) {
const data = res.result as unknown as string
url.value = `${data}?userId=${userStore.userInfo.userExtend.wxId}&token=${tokenStore.validToken}&timestamp=${new Date().getTime()}`
}
if (options.id) {
url.value += `&reportId=${options.id}`
}
if (options.type) {
url.value += `&reportType=${options.type}`
}
if (options.fileId) {
url.value += `&fileId=${options.fileId}`
}
if (options.talentTypeId){
url.value +=`&talentTypeId=${options.talentTypeId}`
}
})
getAssistant().then((res) => {
if (res.code === 200) {
const data = res.result as unknown as string
url.value = `${data}?userId=${userStore.userInfo.userExtend.wxId}&token=${tokenStore.validToken}&timestamp=${new Date().getTime()}`
}
if (options.id) {
url.value += `&reportId=${options.id}`
}
if (options.type) {
url.value += `&reportType=${options.type}`
}
if (options.fileId) {
url.value += `&fileId=${options.fileId}`
}
if (options.talentTypeId) {
url.value += `&talentTypeId=${options.talentTypeId}`
}
})
})
</script>
<template>
<web-view :src="url" @message="handleChildMessage" :update-title="false" />
<web-view :src="url" :update-title="false" @message="handleChildMessage" />
</template>
<style lang="scss" scoped></style>

View File

@ -18,11 +18,14 @@ function navigateToVideoFn() {
}
onLoad(() => {
uni.getChannelsLiveInfo({
uni.getChannelsLiveNoticeInfo({
finderUserName: 'sphju9MCfZetYHP',
success: (res) => {
console.log('res', res)
},
fail: (res) => {
console.log(res)
},
})
})
</script>

View File

@ -1,39 +1,47 @@
<script lang="ts" setup>
import { getNewsDetail } from "@/service"
import { getNewsDetail } from '@/service'
const newsDetail = ref({ title: '', publishTime: "", remark: "",content:"" })
const newsDetail = ref({ title: '', publishTime: '', remark: '', content: '' })
onLoad(options => {
if (options.id) {
getNewsDetail({ query: { id: options.id } }).then(resp => {
if (resp.code == 200) {
newsDetail.value = resp.result
}
})
} else {
uni.showModal({ title: "没有获取到新闻ID" })
}
onLoad((options) => {
if (options.id) {
getNewsDetail({ query: { id: options.id } }).then((resp) => {
if (resp.code === 200) {
newsDetail.value = resp.result
}
})
}
else {
uni.showModal({ title: '没有获取到新闻ID' })
}
})
const navigateToCustom = () => {
uni.navigateTo({url:"/pages-sub/about/onlineCustom"})
function navigateToCustom() {
uni.navigateTo({ url: '/pages-sub/about/onlineCustom' })
}
</script>
<template>
<view class="pt-[30rpx] px-[30rpx] pb-safe">
<view class="text-[40rpx] font-600 text-[#000]">{{ newsDetail.title }}</view>
<view class="text-[#999] text-[28rpx] mt-[8rpx]">发布时间: {{ newsDetail.publishTime }}</view>
<view class="border-b-[2rpx] border-b-dashed border-b-[#D8D8D8] mt-[20rpx] mb-[30rpx]"></view>
<view class="pt-[30rpx]">
<view v-html="newsDetail.content"></view>
</view>
<view class="fixed right-[20rpx] bottom-[120rpx] w-[160rpx] h-[160rpx]" @click="navigateToCustom">
<image src="https://lwzk.ycymedu.com/img/qt/qt_more.png" mode="scaleToFill"
class="w-[160rpx] h-[160rpx]" />
</view>
<view class="px-[30rpx] pt-[30rpx] pb-safe">
<view class="text-[40rpx] text-[#000] font-600">
{{ newsDetail.title }}
</view>
<view class="mt-[8rpx] text-[28rpx] text-[#999]">
发布时间: {{ newsDetail.publishTime }}
</view>
<view class="mb-[30rpx] mt-[20rpx] border-b-[2rpx] border-b-[#D8D8D8] border-b-dashed" />
<view class="pt-[30rpx]">
<!-- <view v-html="newsDetail.content" /> -->
<rich-text :nodes="newsDetail.content" />
</view>
<view class="fixed bottom-[120rpx] right-[20rpx] h-[160rpx] w-[160rpx]" @click="navigateToCustom">
<image
src="https://lwzk.ycymedu.com/img/qt/qt_more.png" mode="scaleToFill"
class="h-[160rpx] w-[160rpx]"
/>
</view>
</view>
</template>
<style lang="scss" scoped></style>
<style lang="scss" scoped></style>

View File

@ -1,22 +1,18 @@
<script lang="ts" setup>
import { trackPromoterRedirect } from '@/service'
import { useInviteStore } from '@/store/invite'
import { getLiveLinks, trackPromoterRedirect } from '@/service'
const inviteStore = useInviteStore()
let referralCode = ''
let weChatLiveId = ''
let douyinLiveUrl = ''
onLoad(() => {
const referralCode = inviteStore.currentPromoter?.referralCode
if (!referralCode) {
return
}
onLoad((options) => {
referralCode = options?.referralCode || ''
trackPromoterRedirect(referralCode).then((resp: any) => {
weChatLiveId = resp?.liveLinks?.wechatLiveUrl || 'sphju9MCfZetYHP'
douyinLiveUrl = resp?.liveLinks?.douyinLiveUrl || ''
getLiveLinks().then((resp) => {
weChatLiveId = resp?.wechatLiveUrl || ''
douyinLiveUrl = resp?.douyinLiveUrl || ''
}).catch((error) => {
console.error('[redirect]', error)
console.error('[live-links]', error)
})
})
@ -28,7 +24,12 @@ function navigateToVideoFn() {
uni.openChannelsLive({
finderUserName: weChatLiveId,
success: () => {
if (!referralCode) {
return
}
trackPromoterRedirect(referralCode).catch((error) => {
console.error('[redirect]', error)
})
},
fail: (err) => {
console.error('跳转失败:', err)
@ -41,6 +42,12 @@ function navigateToDouyinFn() {
uni.showToast({ title: '直播链接获取中,请稍后再试', icon: 'none' })
return
}
if (!referralCode) {
return
}
trackPromoterRedirect(referralCode).catch((error) => {
console.error('[redirect]', error)
})
uni.setClipboardData({
data: douyinLiveUrl,
success: () => {
@ -69,7 +76,7 @@ function navigateToDouyinFn() {
<view class="mt-[80rpx] px-[54rpx]">
<view class="w-full flex items-center justify-center gap-[16rpx] rounded-[210rpx] bg-[#1580FF] py-[30rpx] text-white font-500" @click="navigateToVideoFn">
<view class="h-[39rpx] w-[46rpx] rounded-[210rpx] text-[32rpx]">
<view class="h-[52rpx] w-[46rpx] rounded-[210rpx] text-[32rpx]">
<image
src="https://lw-zk.oss-cn-hangzhou.aliyuncs.com/liebian/shipin.png"
mode="widthFix"
@ -79,7 +86,7 @@ function navigateToDouyinFn() {
</view>
<view class="mt-[48rpx] w-full flex items-center justify-center gap-[16rpx] border-1 border-[#1580FF] rounded-[210rpx] border-solid bg-white py-[30rpx] text-[#1580FF] font-500" @click="navigateToDouyinFn">
<view class="h-[39rpx] w-[46rpx] rounded-[210rpx] text-[32rpx]">
<view class="h-[52rpx] w-[46rpx] rounded-[210rpx] text-[32rpx]">
<image
src="https://lw-zk.oss-cn-hangzhou.aliyuncs.com/liebian/douyin.png"
mode="widthFix"

View File

@ -107,11 +107,6 @@ onShareAppMessage(() => {
imageUrl: 'https://lw-zk.oss-cn-hangzhou.aliyuncs.com/liebian/zhuanshumafenxiangtu.png',
}
})
onShareTimeline(() => {
return {
title: '六纬中考通',
}
})
const canSubmit = computed(() => {
return form.name.trim().length > 0 && /^1[3-9]\d{9}$/.test(form.phone)

View File

@ -1,245 +1,24 @@
<template>
<view class="min-h-screen flex flex-col items-center bg-[#F6F8FB]">
<canvas
id="poster"
canvas-id="poster"
class="block"
:style="{ width: `${canvasWidth}px`, height: `${canvasHeight}px` }"
<view class="min-h-screen flex flex-col items-center bg-[#313131]">
<image
src="https://lw-zk.oss-cn-hangzhou.aliyuncs.com/liebian/zhuanshuyaoqing.png"
mode="widthFix"
/>
<view v-if="errorMessage" class="mt-[40rpx] px-[40rpx] text-center text-[26rpx] text-[#EB5241] leading-[1.5]">
{{ errorMessage }}
</view>
<view class="grid grid-cols-2 mb-[30rpx] mt-auto w-full gap-[20rpx] bg-[#fff] pt-[12rpx] pb-safe">
<button
class="ml-[30rpx] mr-0 h-[88rpx] rounded-[12rpx] border-none bg-[#F5F5F5] text-[32rpx] text-[#333]"
@click="handleRegenerate"
>
重新生成
</button>
<button
class="ml-0 mr-[30rpx] h-[88rpx] rounded-[12rpx] border-none bg-[#1580FF] text-[32rpx] text-[#fff]"
@click="handleSave"
>
保存到手机
</button>
</view>
</view>
</template>
<script lang="ts" setup>
import UQRCode from 'uqrcodejs'
import { useInviteStore } from '@/store/invite'
definePage({
style: {
navigationBarTitleText: '海报测试',
},
const inviteStore = useInviteStore()
const referralCode = inviteStore.currentPromoter?.referralCode
onShareAppMessage(() => {
return {
title: '六纬中考通',
path: `/pages-sub/invite/jump?referralCode=${referralCode}`,
imageUrl: 'https://lw-zk.oss-cn-hangzhou.aliyuncs.com/liebian/yaoqingzhibotu.png',
}
})
const POSTER_IMAGE_URL = 'https://lw-zk.oss-cn-hangzhou.aliyuncs.com/liebian/erweima.png'
const POSTER_DESIGN_HEIGHT_RPX = 1171
// 稿 rpx
const QR_OFFSET_LEFT_RPX = 40
const QR_OFFSET_TOP_RPX = 991
const QR_WIDTH_RPX = 140
const QR_HEIGHT_RPX = 140
const QR_CONTENT = '你好'
const { windowWidth } = uni.getSystemInfoSync()
const canvasWidth = ref(windowWidth)
const canvasHeight = ref(uni.upx2px(POSTER_DESIGN_HEIGHT_RPX))
const errorMessage = ref('')
const saving = ref(false)
const posterReady = ref(false)
onReady(() => {
renderPoster()
})
async function renderPoster() {
errorMessage.value = ''
posterReady.value = false
try {
await nextTick()
const ctx = uni.createCanvasContext('poster')
// 1.
const localPath = await getLocalImagePath(POSTER_IMAGE_URL)
ctx.drawImage(localPath, 0, 0, canvasWidth.value, canvasHeight.value)
// 2.
drawQrCode(ctx, QR_CONTENT, {
x: uni.upx2px(QR_OFFSET_LEFT_RPX),
y: uni.upx2px(QR_OFFSET_TOP_RPX),
width: uni.upx2px(QR_WIDTH_RPX),
height: uni.upx2px(QR_HEIGHT_RPX),
})
// 3.
await new Promise<void>(resolve => ctx.draw(false, () => resolve()))
posterReady.value = true
}
catch (error) {
const message = error instanceof Error ? error.message : '海报绘制失败'
errorMessage.value = message
console.error('[poster]', error)
}
}
function handleRegenerate() {
uni.showLoading({ title: '重新生成中...', mask: true })
renderPoster().finally(() => uni.hideLoading())
}
async function handleSave() {
if (saving.value) {
return
}
saving.value = true
try {
const tempFilePath = await canvasToTempFile('poster')
// #ifdef H5
downloadFileH5(tempFilePath, 'poster.png')
uni.showToast({ title: '已下载到本地', icon: 'success' })
// #endif
// #ifndef H5
await ensureAlbumAuth()
await saveToAlbum(tempFilePath)
uni.showToast({ title: '已保存到相册', icon: 'success' })
// #endif
}
catch (error) {
const message = error instanceof Error ? error.message : '保存失败,请稍后重试'
uni.showToast({ title: message, icon: 'none' })
console.error('[save]', error)
}
finally {
saving.value = false
}
}
interface QrRect {
x: number
y: number
width: number
height: number
}
function drawQrCode(ctx: UniApp.CanvasContext, data: string, rect: QrRect) {
const qr = new UQRCode()
qr.data = data
qr.size = Math.max(rect.width, rect.height) // rect
qr.margin = 0
qr.make()
const moduleCount = qr.moduleCount
const cellW = rect.width / moduleCount
const cellH = rect.height / moduleCount
//
ctx.setFillStyle('#FFFFFF')
ctx.fillRect(rect.x, rect.y, rect.width, rect.height)
// 0.5px
ctx.setFillStyle('#000000')
for (let row = 0; row < moduleCount; row++) {
for (let col = 0; col < moduleCount; col++) {
if (qr.modules[row][col]?.isBlack) {
ctx.fillRect(
rect.x + col * cellW,
rect.y + row * cellH,
cellW + 0.5,
cellH + 0.5,
)
}
}
}
}
function getLocalImagePath(src: string) {
return new Promise<string>((resolve, reject) => {
uni.getImageInfo({
src,
success: res => resolve(res.path),
fail: err => reject(new Error(err?.errMsg || '海报背景图加载失败')),
})
})
}
function canvasToTempFile(canvasId: string) {
return new Promise<string>((resolve, reject) => {
uni.canvasToTempFilePath({
canvasId,
fileType: 'png',
quality: 1,
success: res => resolve(res.tempFilePath),
fail: err => reject(new Error(err?.errMsg || '生成图片失败')),
})
})
}
function saveToAlbum(filePath: string) {
return new Promise<void>((resolve, reject) => {
uni.saveImageToPhotosAlbum({
filePath,
success: () => resolve(),
fail: err => reject(new Error(err?.errMsg || '保存到相册失败')),
})
})
}
//
function ensureAlbumAuth() {
return new Promise<void>((resolve, reject) => {
uni.getSetting({
success: (res) => {
const authorized = res.authSetting['scope.writePhotosAlbum']
if (authorized || authorized === undefined) {
// saveImageToPhotosAlbum
resolve()
return
}
uni.showModal({
title: '提示',
content: '需要您授权保存到相册',
success: (modalRes) => {
if (!modalRes.confirm) {
reject(new Error('已取消保存'))
return
}
uni.openSetting({
success: (settingRes) => {
if (settingRes.authSetting['scope.writePhotosAlbum']) {
resolve()
}
else {
reject(new Error('未授权保存到相册'))
}
},
fail: () => reject(new Error('授权失败')),
})
},
})
},
fail: () => resolve(), //
})
})
}
// #ifdef H5
function downloadFileH5(url: string, filename: string) {
const link = document.createElement('a')
link.href = url
link.download = filename
link.style.display = 'none'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
// #endif
</script>
<style lang="scss" scoped>

View File

@ -1,9 +1,9 @@
<script lang="ts" setup>
import { safeAreaInsets,systemInfo } from '@/utils/systemInfo'
import { storeToRefs } from 'pinia'
import { useUserStore } from '@/store'
import { useTokenStore } from '@/store/token'
import { safeAreaInsets, systemInfo } from '@/utils/systemInfo'
// #ifdef MP-WEIXIN
definePage({
@ -19,7 +19,7 @@ definePage({
style: {
navigationStyle: 'custom',
transparentTitle: 'always',
navigationBarTitleText: ''
navigationBarTitleText: '',
},
excludeLoginPath: true,
})
@ -30,88 +30,107 @@ const tokenStore = useTokenStore()
// 使storeToRefsuserInfo
const { userInfo } = storeToRefs(userStore)
const topMenuList = [{ name: '我的志愿表', img: 'https://lwzk.ycymedu.com/img/qt/wd_biao.png', url: '/pages-sub/me/wishlist' }, { name: '我的测评', img: 'https://lwzk.ycymedu.com/img/qt/wd_cp.png', url: '/pages-sub/me/evaluation' }]
const bottomMenuList = [
{ name: '收藏院校', img: 'https://lwzk.ycymedu.com/img/qt/wd_shoucang.png', url: '/pages-sub/me/starSchool' },
{ name: '联系客服', img: 'https://lwzk.ycymedu.com/img/qt/wd_kefu.png', url: '/pages-sub/about/onlineCustom' },
{ name: '关于我们', img: 'https://lwzk.ycymedu.com/img/qt/wd_guanyu.png', url: '/pages-sub/about/about' },
{ name: '直播码注册', img: 'https://lw-zk.oss-cn-hangzhou.aliyuncs.com/liebian/wd_liebian.png', url: '/pages-sub/invite/login' },
]
const topMenuList = [{ name: '我的志愿表', img: 'https://lwzk.ycymedu.com/img/qt/wd_biao.png',url:'/pages-sub/me/wishlist' }, { name: "我的测评", img: 'https://lwzk.ycymedu.com/img/qt/wd_cp.png',url:'/pages-sub/me/evaluation' }]
const bottomMenuList = [{ name: '收藏院校', img: 'https://lwzk.ycymedu.com/img/qt/wd_shoucang.png',url:"/pages-sub/me/starSchool" }, { name: '联系客服', img: 'https://lwzk.ycymedu.com/img/qt/wd_kefu.png', url:"/pages-sub/about/onlineCustom" }, { name: '关于我们', img: 'https://lwzk.ycymedu.com/img/qt/wd_guanyu.png',url:'/pages-sub/about/about' }]
const navigateToUrl = (url:string) => {
uni.navigateTo({url})
function navigateToUrl(url: string) {
uni.navigateTo({ url })
}
const navigateToUserInfo = () => {
uni.navigateTo({url:"/pages-sub/about/userInfo"})
function navigateToUserInfo() {
uni.navigateTo({ url: '/pages-sub/about/userInfo' })
}
</script>
<template>
<view :class="`custom-background`" :style="{height:`calc(100vh - 50px - ${safeAreaInsets.bottom}px)`}">
<sar-navbar :fixed="true" fixation-style="top:unset;"
:root-style="{ '--sar-navbar-bg': `transparent`, '--sar-navbar-height': `${systemInfo?.statusBarHeight + 44}px` }">
<view class="custom-background" :style="{ height: `calc(100vh - 50px - ${safeAreaInsets.bottom}px)` }">
<sar-navbar
:fixed="true" fixation-style="top:unset;"
:root-style="{ '--sar-navbar-bg': `transparent`, '--sar-navbar-height': `${systemInfo?.statusBarHeight + 44}px` }"
>
<template #title>
<view :style="{ 'padding-top': `${systemInfo?.statusBarHeight}px` }" class="flex justify-center">
我的
</view>
</template>
</sar-navbar>
<view class="flex items-center justify-between px-[50rpx] my-[50rpx]">
<view class="my-[50rpx] flex items-center justify-between px-[50rpx]">
<view class="flex">
<view class="w-[132rpx] h-[132rpx]">
<image :src="`${userInfo.avatar}`" mode="scaleToFill"
class="w-[132rpx] h-[132rpx] rounded-full" />
<view class="h-[132rpx] w-[132rpx]">
<image
:src="`${userInfo.avatar}`" mode="scaleToFill"
class="h-[132rpx] w-[132rpx] rounded-full"
/>
</view>
<view class="ml-[32rpx]">
<view class="text-[44rpx] font-500 mb-[10rpx]">{{ userInfo.nickName }}</view>
<view class="text-[#666] text-[32rpx]">{{ userInfo.mobile }}</view>
<view class="mb-[10rpx] text-[44rpx] font-500">
{{ userInfo.nickName }}
</view>
<view class="text-[32rpx] text-[#666]">
{{ userInfo.mobile }}
</view>
</view>
</view>
<view class="flex flex-col items-center" @click="navigateToUserInfo">
<view class="w-[48rpx] h-[48rpx]">
<image src="https://lwzk.ycymedu.com/img/qt/wd_bianji.png" mode="scaleToFill"
class="w-[48rpx] h-[48rpx]" />
<view class="h-[48rpx] w-[48rpx]">
<image
src="https://lwzk.ycymedu.com/img/qt/wd_bianji.png" mode="scaleToFill"
class="h-[48rpx] w-[48rpx]"
/>
</view>
<view class="text-[30rpx] text-[#333]">
编辑
</view>
<view class="text-[30rpx] text-[#333]">编辑</view>
</view>
</view>
<view class="grid grid-cols-2 gap-[28rpx] items-center justify-between px-[30rpx]">
<view class="grid grid-cols-2 items-center justify-between gap-[28rpx] px-[30rpx]">
<view
class="rounded-[20rpx] flex items-center px-[50rpx] py-[44rpx] bg-[#E4EEFF] border-[2rpx] border-solid border-white"
v-for="(value, index) in topMenuList" :key="index" @click="navigateToUrl(value.url)">
<view class="w-[48rpx] h-[48rpx] mr-[10rpx]">
<image :src="value.img" mode="scaleToFill" class="w-[48rpx] h-[48rpx]" />
v-for="(value, index) in topMenuList"
:key="index" class="flex items-center border-[2rpx] border-white rounded-[20rpx] border-solid bg-[#E4EEFF] px-[50rpx] py-[44rpx]" @click="navigateToUrl(value.url)"
>
<view class="mr-[10rpx] h-[48rpx] w-[48rpx]">
<image :src="value.img" mode="scaleToFill" class="h-[48rpx] w-[48rpx]" />
</view>
<view>{{ value.name }}</view>
</view>
</view>
<view class="px-[30rpx] pt-[30rpx] mx-[30rpx] rounded-[20rpx] bg-white mt-[30rpx]">
<view class="text-[34rpx] font-500">其他功能</view>
<view class="flex items-center py-[34rpx] not-last:border-b-[2rpx] not-last:border-b-solid not-last:border-b-[#EDEDED]" v-for="(value,index) in bottomMenuList" :key="index" @click="navigateToUrl(value.url)">
<view class="w-[44rpx] h-[44rpx] flex items-center">
<view class="mx-[30rpx] mt-[30rpx] rounded-[20rpx] bg-white px-[30rpx] pt-[30rpx]">
<view class="text-[34rpx] font-500">
其他功能
</view>
<view v-for="(value, index) in bottomMenuList" :key="index" class="flex items-center py-[34rpx] not-last:border-b-[2rpx] not-last:border-b-[#EDEDED] not-last:border-b-solid" @click="navigateToUrl(value.url)">
<view class="h-[44rpx] w-[44rpx] flex items-center">
<image
:src="value.img"
mode="scaleToFill"
class="w-[44rpx] h-[44rpx]"
class="h-[44rpx] w-[44rpx]"
/>
</view>
<view class="text-[32rpx] ml-[16rpx]">{{ value.name }}</view>
<view class="w-[18rpx] h-[36rpx] ml-auto flex items-center">
<view class="ml-[16rpx] text-[32rpx]">
{{ value.name }}
</view>
<view class="ml-auto h-[36rpx] w-[18rpx] flex items-center">
<image
src="https://lwzk.ycymedu.com/img/qt/tb_jiantou.png"
mode="scaleToFill"
class="w-[18rpx] h-[36rpx]"
class="h-[36rpx] w-[18rpx]"
/>
</view>
</view>
</view>
</view>
</template>
<style lang="scss" scoped>
.custom-background {
background: linear-gradient(180deg, #C7E0FF 0%, #f8f8f8 530rpx);
background: linear-gradient(180deg, #c7e0ff 0%, #f8f8f8 530rpx);
background-position: 50% 50%;
background-origin: padding-box;
background-clip: border-box;

View File

@ -81,3 +81,36 @@ export const trackPromoterRedirect = (referralCode: string) => {
})
})
}
export interface LiveLinks {
id: string
wechatLiveUrl: string
douyinLiveUrl: string
updatedAt: string
}
const LIVE_LINKS_API_URL = 'https://liveroom.ycymedu.com/api/h5/live-links'
export const getLiveLinks = () => {
return new Promise<LiveLinks>((resolve, reject) => {
uni.request({
url: LIVE_LINKS_API_URL,
method: 'GET',
header: {
'x-api-key': INVITE_API_KEY,
},
success: (res) => {
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(res.data as LiveLinks)
return
}
reject(new Error(getInviteErrorMessage(res.data)))
},
fail: () => {
reject(new Error('请求失败,请稍后重试'))
},
})
})
}

77
src/store/ai.ts Normal file
View File

@ -0,0 +1,77 @@
import { defineStore } from 'pinia'
import { computed, ref } from 'vue'
export type AiRole = 'user' | 'assistant' | 'system'
export interface AiMessage {
id: string
role: AiRole
content: string
/** 是否仍在流式生成中,用于 UI 展示打字光标 */
pending?: boolean
/** 标记本条消息是否出错UI 显示重试按钮 */
error?: boolean
createdAt: number
}
export const useAiStore = defineStore(
'ai',
() => {
const messages = ref<AiMessage[]>([])
/** 一组对话共用同一个 thread跨多轮维持上下文 */
const threadId = ref('')
const messageCount = computed(() => messages.value.length)
function ensureThreadId() {
if (!threadId.value) {
threadId.value = `thread_id_${Date.now()}${Math.floor(Math.random() * 10000)}`
}
return threadId.value
}
function addMessage(message: AiMessage) {
messages.value.push(message)
}
function updateMessage(id: string, patch: Partial<AiMessage>) {
const target = messages.value.find(m => m.id === id)
if (!target) {
return
}
Object.assign(target, patch)
}
function appendDelta(id: string, delta: string) {
const target = messages.value.find(m => m.id === id)
if (!target) {
return
}
target.content += delta
}
function removeMessage(id: string) {
messages.value = messages.value.filter(m => m.id !== id)
}
function clear() {
messages.value = []
threadId.value = ''
}
return {
messages,
threadId,
messageCount,
ensureThreadId,
addMessage,
updateMessage,
appendDelta,
removeMessage,
clear,
}
},
{
persist: true,
},
)

View File

@ -16,6 +16,7 @@ setActivePinia(store)
export default store
// 模块统一导出
export * from './ai'
export * from './invite'
export * from './token'
export * from './user'

View File

@ -158,9 +158,9 @@ export const isDoubleTokenMode = import.meta.env.VITE_AUTH_MODE === 'double'
*/
export const HOME_PAGE = `/${(pages as PageMetaDatum[]).find(page => page.type === 'home')?.path || (pages as PageMetaDatum[])[0].path}`
export const checkEmptyValues=(obj: Record<string, any>, keys: string[]): boolean=> {
return keys.some(key => {
const value = obj[key];
return value === '' || value === null || value === undefined;
});
}
export function checkEmptyValues(obj: Record<string, any>, keys: string[]): boolean {
return keys.some((key) => {
const value = obj[key]
return value === '' || value === null || value === undefined
})
}

View File

@ -0,0 +1,231 @@
// components/agent-ui-new/chatFIle/chatFile.js
import { getCloudInstance, compareVersions, commonRequest } from "../tools";
Component({
lifetimes: {
attached: async function () {
console.log("enableDel", this.data.enableDel);
const { tempFileName, rawFileName, rawType, tempPath, fileId, botId, status } = this.data.fileData;
const type = this.getFileType(rawFileName || tempFileName);
console.log("type", type);
if (!fileId) {
this.setData({
iconPath: "../imgs/" + type + ".svg",
});
this.triggerEvent("changeChild", { tempId: this.data.fileData.tempId, status: "uploading" });
}
if (fileId && status === "parsed") {
this.setData({
iconPath: "../imgs/" + type + ".svg",
});
return;
}
const cloudInstance = await getCloudInstance();
// console.log('file', cloudInstance)
// 上传云存储获取 fileId
// console.log('rawFileName tempFileName tempPath', rawFileName, tempFileName, tempPath)
cloudInstance.uploadFile({
cloudPath: this.generateCosUploadPath(
botId,
rawFileName ? rawFileName.split(".")[0] + "-" + tempFileName : tempFileName
), // 云上文件路径
filePath: tempPath,
success: async (res) => {
const appBaseInfo = wx.getAppBaseInfo();
const fileId = res.fileID;
console.log("当前版本", appBaseInfo.SDKVersion);
if (botId.startsWith("ibot")) {
this.triggerEvent("changeChild", { tempId: this.data.fileData.tempId, fileId, status: "parsed" });
} else {
this.triggerEvent("changeChild", { tempId: this.data.fileData.tempId, status: "parsing" });
commonRequest({
path: `bots/${botId}/files`,
data: {
fileList: [
{
fileName: rawFileName || tempFileName,
fileId,
type: rawType,
},
],
}, // any
method: "POST",
timeout: 60000,
success: (res) => {
console.log("resolve agent file res", res);
this.triggerEvent("changeChild", { tempId: this.data.fileData.tempId, fileId, status: "parsed" });
},
fail: (e) => {
console.log("e", e);
this.triggerEvent("changeChild", { tempId: this.data.fileData.tempId, fileId, status: "parseFailed" });
},
complete: () => {},
header: {},
});
}
},
fail: (err) => {
console.error("上传失败:", err);
},
});
},
},
observers: {
"fileData.status": function (status) {
this.setData({
statusTxt: this.getFormatStatusText(status),
});
},
},
/**
* 组件的属性列表
*/
properties: {
enableDel: {
type: Boolean,
value: false,
},
fileData: {
type: Object,
value: {
tempId: "",
rawType: "",
tempFileName: "",
rawFileName: "",
tempPath: "",
fileSize: 0,
fileUrl: "",
fileId: "",
status: "",
},
},
},
/**
* 组件的初始数据
*/
data: {
formatSize: "",
iconPath: "../imgs/file.svg",
statusTextMap: {
uploading: "上传中",
parsing: "解析中",
parseFailed: "解析失败",
},
statusTxt: "",
},
/**
* 组件的方法列表
*/
methods: {
getFormatStatusText: function (status) {
if (status === "parsed") {
return this.transformSize(this.data.fileData.fileSize);
}
return this.data.statusTextMap[status] || "";
},
generateCosUploadPath: function (botId, fileName) {
return `agent_file/${botId}/${fileName}`;
},
// 提取文件后缀
getFileType: function (fileName) {
let index = fileName.lastIndexOf(".");
const fileExt = fileName.substring(index + 1);
if (fileExt === "docx" || fileExt === "doc") {
return "word";
}
if (fileExt === "xlsx" || fileExt === "xls" || fileExt === "csv") {
return "excel";
}
if (fileExt === "png" || fileExt === "jpg" || fileExt === "jpeg" || fileExt === "svg") {
return "image";
}
if (fileExt === "ppt" || fileExt === "pptx") {
return "ppt";
}
if (fileExt === "pdf") {
return "pdf";
}
return "file";
},
// 转换文件大小原始单位为B
transformSize: function (size) {
if (size < 1024) {
return size + "B";
} else if (size < 1024 * 1024) {
return (size / 1024).toFixed(2) + "KB";
} else {
return (size / 1024 / 1024).toFixed(2) + "MB";
}
},
removeFileFromParents: function () {
console.log("remove", this.data.fileData);
this.triggerEvent("removeChild", { tempId: this.data.fileData.tempId });
},
openFileByWx: function (tempPath) {
const fileExt = tempPath.split(".")[1];
if (["doc", "docx", "xls", "xlsx", "ppt", "pptx", "pdf"].includes(fileExt)) {
wx.openDocument({
filePath: tempPath,
success: function (res) {
console.log("打开文档成功");
},
fail: function (err) {
console.log("打开文档失败", err);
},
});
} else {
wx.showModal({
content: "当前支持预览文件类型为 pdf、doc、docx、ppt、pptx、xls、xlsx",
showCancel: false,
confirmText: "确定",
});
}
},
previewImageByWx: function (fileId) {
wx.previewImage({
urls: [fileId],
showmenu: true,
success: function (res) {
console.log("previewImage res", res);
},
fail: function (e) {
console.log("previewImage e", e);
},
});
},
openFile: async function () {
if (this.data.fileData.tempPath) {
// 本地上传的文件
if (this.data.fileData.rawType === "file") {
this.openFileByWx(this.data.fileData.tempPath);
} else {
console.log("fileId", this.data.fileData.fileId);
if (this.data.fileData.fileId) {
this.previewImageByWx(this.data.fileData.fileId);
}
}
} else if (this.data.fileData.fileId) {
// 针对历史记录中带cloudID的处理历史记录中附带的文件
const cloudInsatnce = await getCloudInstance();
cloudInsatnce.downloadFile({
fileID: this.data.fileData.fileId,
success: (res) => {
console.log("download res", res);
if (this.data.fileData.rawType === "file") {
this.openFileByWx(res.tempFilePath);
} else {
this.previewImageByWx(this.data.fileData.fileId);
}
},
fail: (err) => {
console.log("download err", err);
},
});
}
},
},
});

View File

@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

View File

@ -0,0 +1,14 @@
<!--components/agent-ui-new/chatFIle/chatFile.wxml-->
<!-- <text>components/agent-ui-new/chatFIle/chatFile.wxml</text> -->
<view class="chat_file" bind:tap="openFile">
<view class="chat_file__content">
<image class="chat_file__icon" src="{{iconPath}}" />
<view class="chat_file__info">
<view class="chat_file__name">{{fileData.rawFileName || fileData.tempFileName}}</view>
<view class="chat_file__size">{{statusTxt}}
<image wx:if="{{fileData.status === 'uploading' || fileData.status === 'parsing'}}" style="width: 15px;height:15px;margin-left: 5px" src="../imgs/loading.svg" mode=""/>
</view>
</view>
</view>
<image wx:if="{{enableDel}}" bind:tap="removeFileFromParents" style="width: 15px;height: 15px;position: absolute;top: -7px;right: -7px" src="../imgs/close-filled.png" mode="aspectFill"/>
</view>

View File

@ -0,0 +1,49 @@
/* components/agent-ui-new/chatFIle/chatFile.wxss */
.chat_file {
padding: 16rpx 24rpx;
background: #ffffff;
border-radius: 12rpx;
/* box-shadow: 0 1px 8px rgba(0, 0, 0, 0.253); */
/* margin: 0rpx 5rpx; */
/* max-width: 110px; */
width: 110px;
position: relative;
background-color: #f3f4f6;
}
.chat_file:active {
background: #f9f9f9;
}
.chat_file__content {
display: flex;
align-items: center;
gap: 24rpx;
}
.chat_file__icon {
width: 60rpx;
height: 60rpx;
flex-shrink: 0;
}
.chat_file__info {
flex: 1;
min-width: 0;
}
.chat_file__name {
font-size: 28rpx;
color: #333333;
margin-bottom: 8rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.chat_file__size {
font-size: 24rpx;
color: #999999;
display: flex;
align-items: center;
}

View File

@ -0,0 +1,44 @@
// components/agent-ui/collapsibleCard/index.js
Component({
/**
* 组件的属性列表
*/
properties: {
initStatus: {
type: Boolean,
value: false
},
showBgColor:{
type: Boolean,
value: false
},
showExpandIcon: {
type: Boolean,
value: true
}
},
/**
* 组件的初始数据
*/
data: {
collapsedStatus: false
},
lifetimes: {
attached() {
this.setData({ collapsedStatus: this.properties.initStatus })
}
},
/**
* 组件的方法列表
*/
methods: {
changeCollapsedStatus: function () {
this.setData({ collapsedStatus: !this.data.collapsedStatus })
}
},
options: {
multipleSlots: true
}
})

View File

@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

View File

@ -0,0 +1,10 @@
<!--components/agent-ui/collapsibleCard/index.wxml-->
<view class="collapse" style="{{collapsedStatus&&showBgColor?'background-color: #f5f5f5;':''}}">
<view class="collapse-header" bind:tap="changeCollapsedStatus">
<image wx:if="{{showExpandIcon}}" src="../imgs/arrow.svg" mode="aspectFill" style="width: 16px;height: 16px;transform: rotate({{collapsedStatus?360:270}}deg);" />
<slot name="title"></slot>
</view>
<block wx:if="{{collapsedStatus}}">
<slot name="content"></slot>
</block>
</view>

View File

@ -0,0 +1,14 @@
/* components/agent-ui/collapsibleCard/index.wxss */
.collapse{
border-radius: 8px;
margin-bottom: 12px;
}
.collapse-header {
display: inline-flex;
align-items: center;
gap: 8px;
background-color: #f5f5f5;
justify-content: space-between;
padding: 18rpx 26rpx;
border-radius: 8px;
}

View File

@ -0,0 +1,18 @@
Component({
properties: {
name: {
type: String,
value: "",
},
toolParams: {
type: Object,
value: {},
},
toolData: {
type: Object,
value: {},
},
},
data: {},
lifetimes: {},
});

View File

@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

View File

@ -0,0 +1,27 @@
<!--components/agent-ui-new/chatFIle/chatFile.wxml-->
<!-- <text>components/agent-ui-new/chatFIle/chatFile.wxml</text> -->
<!-- <block>
<view wx:if="{{name === 'maps_geo' || name === 'maps_direction_driving'}}">
<custom-map name="{{name}}" toolData="{{toolData}}"></custom-map>
</view>
<view wx:if="{{name === 'maps_weather'}}">
<custom-weather name="{{name}}" toolData="{{toolData}}"></custom-weather>
</view>
<view wx:if="{{name === 'map_search_places'}}">
<custom-business-list name="{{name}}" toolData="{{toolData}}"></custom-business-list>
<custom-food-list name="{{name}}" toolData="{{toolData}}"></custom-food-list>
</view>
</block> -->
<block>
<view class="customCard">
<custom-map wx:if="{{name === 'geocoder' || name === 'placeSearchNearby' || 'directionDriving'}}" name="{{name}}" toolParams="{{toolParams}}" toolData="{{toolData}}"></custom-map>
</view>
<view class="customCard">
<custom-weather wx:if="{{name === 'weather'}}" name="{{name}}" toolData="{{toolData}}"></custom-weather>
</view>
<view class="customCard" wx:if="{{name === 'placeSearchNearby'}}">
<custom-business-list name="{{name}}" toolData="{{toolData}}"></custom-business-list>
</view>
<!-- 用户可类似添加自定义组件 -->
</block>

View File

@ -0,0 +1,3 @@
.customCard {
margin: 15px 0px;
}

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12.0003 0.630371L14.9029 8.98093L23.7417 9.16105L16.6969 14.5021L19.2569 22.964L12.0003 17.9144L4.74363 22.964L7.30367 14.5021L0.258789 9.16105L9.09761 8.98093L12.0003 0.630371ZM12.0003 6.72181L10.5298 10.9522L6.05209 11.0434L9.62099 13.7492L8.32409 18.0359L12.0003 15.4778L15.6764 18.0359L14.3795 13.7492L17.9484 11.0434L13.4707 10.9522L12.0003 6.72181Z" fill="#f2db4a" />
</svg>

After

Width:  |  Height:  |  Size: 473 B

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12.0003 0.630371L14.9029 8.98093L23.7417 9.16105L16.6969 14.5021L19.2569 22.964L12.0003 17.9144L4.74363 22.964L7.30367 14.5021L0.258789 9.16105L9.09761 8.98093L12.0003 0.630371ZM12.0003 6.72181L10.5298 10.9522L6.05209 11.0434L9.62099 13.7492L8.32409 18.0359L12.0003 15.4778L15.6764 18.0359L14.3795 13.7492L17.9484 11.0434L13.4707 10.9522L12.0003 6.72181Z" fill="black" />
</svg>

After

Width:  |  Height:  |  Size: 471 B

View File

@ -0,0 +1,277 @@
// pages/components/feedback/index.js
Component({
/**
* 组件的属性列表
*/
properties: {
isShowFeedback: {
type: Boolean,
value: false
},
feedbackRecordId: {
type: String,
value: ''
},
feedbackType: {
type: String,
value: ''
},
botId: {
type: String,
value: ''
},
input: {
type: String,
value: ""
},
aiAnswer: {
type: String,
value: ''
}
},
/**
* 组件的初始数据
*/
data: {
upVote: [
{
"selected": false,
"value": "准确有效"
},
{
"selected": false,
"value": "回答全面"
},
{
"selected": false,
"value": "立场正确"
},
{
"selected": false,
"value": "格式规范"
},
{
"selected": false,
"value": "专业性强"
},
{
"selected": false,
"value": "富有创意"
},
{
"selected": false,
"value": "表达清晰"
},
{
"selected": false,
"value": "值得信赖"
},
{
"selected": false,
"value": "高效"
},
{
"selected": false,
"value": "满意"
}
],
downVote: [
{
"selected": false,
"value": "理解错误"
},
{
"selected": false,
"value": "未识别问题"
},
{
"selected": false,
"value": "事实错误"
},
{
"selected": false,
"value": "推理错误"
},
{
"selected": false,
"value": "内容不完整"
},
{
"selected": false,
"value": "不专业"
},
{
"selected": false,
"value": "违法有害"
},
{
"selected": false,
"value": "格式错误"
},
{
"selected": false,
"value": "乱码"
},
{
"selected": false,
"value": "内容重复"
}
],
score: 5,
message: ""
},
observers:{
"feedbackType":function (value) {
this.setData({score:value==='upvote'?5:1})
}
},
/**
* 组件的方法列表
*/
methods: {
reset: function () {
this.setData({
upVote: [
{
"selected": false,
"value": "准确有效"
},
{
"selected": false,
"value": "回答全面"
},
{
"selected": false,
"value": "立场正确"
},
{
"selected": false,
"value": "格式规范"
},
{
"selected": false,
"value": "专业性强"
},
{
"selected": false,
"value": "富有创意"
},
{
"selected": false,
"value": "表达清晰"
},
{
"selected": false,
"value": "值得信赖"
},
{
"selected": false,
"value": "高效"
},
{
"selected": false,
"value": "满意"
}
],
downVote: [
{
"selected": false,
"value": "理解错误"
},
{
"selected": false,
"value": "未识别问题"
},
{
"selected": false,
"value": "事实错误"
},
{
"selected": false,
"value": "推理错误"
},
{
"selected": false,
"value": "内容不完整"
},
{
"selected": false,
"value": "不专业"
},
{
"selected": false,
"value": "违法有害"
},
{
"selected": false,
"value": "格式错误"
},
{
"selected": false,
"value": "乱码"
},
{
"selected": false,
"value": "内容重复"
}
],
score: 5,
message: ""
})
},
onChangeScore: function (e) {
const { score } = e.currentTarget.dataset
this.setData({ score })
},
onSelect: function (e) {
const { item } = e.currentTarget.dataset
const newArr = [...this.data.feedbackType === 'upvote' ? this.data.upVote : this.data.downVote]
const [selectedItem] = newArr.filter(i => i.value === item.value)
selectedItem.selected = !selectedItem.selected
if (this.data.feedbackType === 'upvote') {
this.setData({ upVote: newArr })
} else {
this.setData({ downVote: newArr })
}
},
inputChange: function (e) {
const value = e.detail.value
this.setData({ message: value })
},
closeShowFeedback: function () {
this.triggerEvent('close')
},
submitFeedback: async function () {
const res = await wx.cloud.extend.AI.bot.sendFeedback({
userFeedback: {
botId: this.data.botId,
recordId: this.data.feedbackRecordId,
comment: this.data.message,
rating: this.data.score,
tags: this.data.feedbackType === 'upvote' ? this.data.upVote.filter(item => item.selected).map(item => item.value) : this.data.downVote.filter(item => item.selected).map(item => item.value),
aiAnswer: this.data.aiAnswer,
input: this.data.input,
type: this.data.feedbackType === 'upvote' ? "upvote" : 'downvote',
},
botId: this.data.botId
});
if (res.status === 'success') {
wx.showToast({
title: "感谢反馈",
icon: "success",
});
} else {
wx.showToast({
title: "反馈失败",
icon: "fail",
});
}
this.reset();
// console.log(res)
this.triggerEvent("close")
}
}
})

View File

@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

View File

@ -0,0 +1,35 @@
<view class="feedback-modal" wx:if="{{isShowFeedback}}">
<view class="feedback">
<view class="feedback-header">
感谢您的宝贵反馈,我们会不断改进服务
</view>
<view class="feedback-body">
<view class="item-box">
<view class="item-title">评分</view>
<view style="display: flex; gap: 14rpx;">
<block wx:for="{{[1,2,3,4,5]}}" wx:key="*this">
<image src="{{item<=score?'./imgs/star-highlight.svg':'./imgs/star.svg'}}" mode="aspectFill" class="star" bind:touchend="onChangeScore" data-score="{{item}}" />
</block>
</view>
</view>
<view class="item-box">
<view class="item-title">回答内容</view>
<view>
<block wx:for="{{feedbackType==='upvote'?upVote:downVote}}" wx:key="value">
<view class="{{item.selected?'vote-item-highlight':'vote-item-normal'}}" bind:tap="onSelect" data-item="{{item}}">{{item.value}}</view>
</block>
</view>
</view>
<view class="item-box">
<view class="item-title">反馈建议</view>
<view>
<textarea value="{{message}}" class="feedback-textarea" maxlength="140" bindinput="inputChange"/>
</view>
</view>
</view>
<view class="feedback-footer">
<view class="btn-cancel" bind:tap="closeShowFeedback">取消</view>
<view class="btn-submit" bind:tap="submitFeedback">提交反馈</view>
</view>
</view>
</view>

View File

@ -0,0 +1,91 @@
/* pages/components/feedback/index.wxss */
.feedback-modal {
position: fixed;
top: 0px;
left: 0px;
width: 750rpx;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
display: flex;
justify-content: center;
align-items: center;
}
.feedback {
width: 520rpx;
max-height: 70%;
overflow-y: auto;
background-color: #fff;
border-radius: 24rpx;
padding: 40rpx;
}
.feedback-header{
font-weight: 500;
font-size: 32rpx;
text-align: justify;
}
.feedback-body{
font-size: 28rpx;
}
.item-box{
padding: 28rpx 0px;
}
.item-title{
font-size: 28rpx;
padding-bottom: 28rpx;
}
.star{
width: 40rpx;
height: 40rpx;
}
.vote-item-normal{
display: inline-block;
background-color: rgba(243, 243, 243, 1);
height: 48rpx;
line-height: 48rpx;
border-radius: 24rpx;
padding: 4rpx 24rpx;
margin-right: 16rpx;
margin-bottom: 16rpx;
}
.vote-item-highlight{
display: inline-block;
background-color: rgba(0, 82, 217, 0.1);
height: 48rpx;
line-height: 48rpx;
border-radius: 24rpx;
padding: 4rpx 24rpx;
margin-right: 16rpx;
margin-bottom: 16rpx;
color: rgb(0, 82, 217);
}
.feedback-textarea{
width: 100%;
height: 150rpx;
border-radius: 16rpx;
border: 1px solid #ccc;
box-sizing: border-box;
padding: 16rpx;
}
.feedback-footer{
font-size: 28rpx;
display: flex;
justify-content: flex-end;
gap: 24rpx;
}
.btn-cancel{
box-sizing: border-box;
background-color: #fff;
border: 1px solid #eee;
padding: 6rpx 24rpx;
border-radius: 6rpx;
line-height: 48rpx;
}
.btn-submit{
box-sizing: border-box;
padding: 6rpx 24rpx;
border-radius: 6rpx;
color: #fff;
background-color: #0052d9;
line-height: 48rpx;
}

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M17.5001 8.08575L12.0002 13.5858L6.50015 8.08576L5.08594 9.49997L12.0002 16.4142L18.9144 9.49997L17.5001 8.08575Z" fill="black" />
</svg>

After

Width:  |  Height:  |  Size: 244 B

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7.88197 1.99988H16.118L17.618 4.99988H23V20.9999H1V4.99988H6.38197L7.88197 1.99988ZM9.11803 3.99988L7.61803 6.99988H3V18.9999H21V6.99988H16.382L14.882 3.99988H9.11803ZM12 9.49988C10.3431 9.49988 9 10.843 9 12.4999C9 14.1567 10.3431 15.4999 12 15.4999C13.6569 15.4999 15 14.1567 15 12.4999C15 10.843 13.6569 9.49988 12 9.49988ZM7 12.4999C7 9.73845 9.23858 7.49988 12 7.49988C14.7614 7.49988 17 9.73845 17 12.4999C17 15.2613 14.7614 17.4999 12 17.4999C9.23858 17.4999 7 15.2613 7 12.4999Z" fill="rgb(95, 114, 146)" />
</svg>

After

Width:  |  Height:  |  Size: 630 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><rect width="2.8" height="12" x="1" y="6" fill="#e84f50"><animate attributeName="y" begin="svgSpinnersBarsScaleMiddle0.begin+0.4s" calcMode="spline" dur="0.6s" keySplines=".14,.73,.34,1;.65,.26,.82,.45" values="6;1;6"/><animate attributeName="height" begin="svgSpinnersBarsScaleMiddle0.begin+0.4s" calcMode="spline" dur="0.6s" keySplines=".14,.73,.34,1;.65,.26,.82,.45" values="12;22;12"/></rect><rect width="2.8" height="12" x="5.8" y="6" fill="#e84f50"><animate attributeName="y" begin="svgSpinnersBarsScaleMiddle0.begin+0.2s" calcMode="spline" dur="0.6s" keySplines=".14,.73,.34,1;.65,.26,.82,.45" values="6;1;6"/><animate attributeName="height" begin="svgSpinnersBarsScaleMiddle0.begin+0.2s" calcMode="spline" dur="0.6s" keySplines=".14,.73,.34,1;.65,.26,.82,.45" values="12;22;12"/></rect><rect width="2.8" height="12" x="10.6" y="6" fill="#e84f50"><animate id="svgSpinnersBarsScaleMiddle0" attributeName="y" begin="0;svgSpinnersBarsScaleMiddle1.end-0.1s" calcMode="spline" dur="0.6s" keySplines=".14,.73,.34,1;.65,.26,.82,.45" values="6;1;6"/><animate attributeName="height" begin="0;svgSpinnersBarsScaleMiddle1.end-0.1s" calcMode="spline" dur="0.6s" keySplines=".14,.73,.34,1;.65,.26,.82,.45" values="12;22;12"/></rect><rect width="2.8" height="12" x="15.4" y="6" fill="#e84f50"><animate attributeName="y" begin="svgSpinnersBarsScaleMiddle0.begin+0.2s" calcMode="spline" dur="0.6s" keySplines=".14,.73,.34,1;.65,.26,.82,.45" values="6;1;6"/><animate attributeName="height" begin="svgSpinnersBarsScaleMiddle0.begin+0.2s" calcMode="spline" dur="0.6s" keySplines=".14,.73,.34,1;.65,.26,.82,.45" values="12;22;12"/></rect><rect width="2.8" height="12" x="20.2" y="6" fill="#e84f50"><animate id="svgSpinnersBarsScaleMiddle1" attributeName="y" begin="svgSpinnersBarsScaleMiddle0.begin+0.4s" calcMode="spline" dur="0.6s" keySplines=".14,.73,.34,1;.65,.26,.82,.45" values="6;1;6"/><animate attributeName="height" begin="svgSpinnersBarsScaleMiddle0.begin+0.4s" calcMode="spline" dur="0.6s" keySplines=".14,.73,.34,1;.65,.26,.82,.45" values="12;22;12"/></rect></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 3C7.02944 3 3 7.02944 3 12C3 14.3966 3.93542 16.5725 5.46305 18.1862L5.96701 18.7185L4.69951 21H12C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3ZM1 12C1 5.92487 5.92487 1 12 1C18.0751 1 23 5.92487 23 12C23 18.0751 18.0751 23 12 23H1.30049L3.51936 19.006C1.94632 17.1038 1 14.6615 1 12ZM13 8V11H16V13H13V16H11V13H8V11H11V8H13Z" fill="#4d6bfe" />
</svg>

After

Width:  |  Height:  |  Size: 470 B

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 3C7.02944 3 3 7.02944 3 12C3 14.3966 3.93542 16.5725 5.46305 18.1862L5.96701 18.7185L4.69951 21H12C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3ZM1 12C1 5.92487 5.92487 1 12 1C18.0751 1 23 5.92487 23 12C23 18.0751 18.0751 23 12 23H1.30049L3.51936 19.006C1.94632 17.1038 1 14.6615 1 12ZM13 8V11H16V13H13V16H11V13H8V11H11V8H13Z" fill="rgb(95, 114, 146)" />
</svg>

After

Width:  |  Height:  |  Size: 480 B

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 3C7.02944 3 3 7.02944 3 12C3 14.3966 3.93542 16.5725 5.46305 18.1862L5.96701 18.7185L4.69951 21H12C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3ZM1 12C1 5.92487 5.92487 1 12 1C18.0751 1 23 5.92487 23 12C23 18.0751 18.0751 23 12 23H1.30049L3.51936 19.006C1.94632 17.1038 1 14.6615 1 12ZM13 5.5V11.5858L16.4142 15L15 16.4142L11 12.4142V5.5H13Z" fill="rgb(160, 160, 160)" />
</svg>

After

Width:  |  Height:  |  Size: 497 B

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M20.985 7.37845L10.3784 17.985L4.0144 11.6211L5.42862 10.2069L10.3784 15.1566L19.5708 5.96423L20.985 7.37845Z" fill="green" />
</svg>

After

Width:  |  Height:  |  Size: 237 B

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9 1L15 1L15 9.5L21 9.5L21 23L3 23L3 9.5L9 9.5L9 1ZM11 3L11 11.5L5 11.5V14L19 14L19 11.5H13L13 3L11 3ZM19 16L5 16L5 21H14V18L16 18V21H19V16Z" fill="rgb(95, 114, 146)" />
</svg>

After

Width:  |  Height:  |  Size: 283 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 B

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7.0502 5.63611L11.9999 10.5859L16.9497 5.63611L18.3639 7.05032L13.4142 12.0001L18.3639 16.9498L16.9497 18.364L11.9999 13.4143L7.0502 18.364L5.63599 16.9498L10.5857 12.0001L5.63599 7.05032L7.0502 5.63611Z" fill="red" />
</svg>

After

Width:  |  Height:  |  Size: 333 B

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7.0502 5.63611L11.9999 10.5859L16.9497 5.63611L18.3639 7.05032L13.4142 12.0001L18.3639 16.9498L16.9497 18.364L11.9999 13.4143L7.0502 18.364L5.63599 16.9498L10.5857 12.0001L5.63599 7.05032L7.0502 5.63611Z" fill="black" />
</svg>

After

Width:  |  Height:  |  Size: 335 B

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M2 2H15V7.5H13V4H4V13H7.5V15H2V2ZM9 9H22V22H9V9ZM11 11V20H20V11H11Z" fill="#8b8b8b" />
</svg>

After

Width:  |  Height:  |  Size: 197 B

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M16.4278 1.96289L22.0381 7.57319L7.61066 22.0007L2.00037 22.0007L2.00037 16.3904L16.4278 1.96289ZM16.4278 4.79132L13.646 7.57319L16.4278 10.3551L19.2097 7.57319L16.4278 4.79132ZM15.0136 11.7693L12.2318 8.9874L4.00037 17.2188L4.00037 20.0007H6.78224L15.0136 11.7693ZM22.2252 22.0007H12.6821V20.0007L22.2252 20.0007V22.0007Z" fill="black" />
</svg>

After

Width:  |  Height:  |  Size: 453 B

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12.0001 3C7.02956 3 3.00012 7.02944 3.00012 12C3.00012 16.9706 7.02956 21 12.0001 21C16.9707 21 21.0001 16.9706 21.0001 12C21.0001 7.02944 16.9707 3 12.0001 3ZM1.00012 12C1.00012 5.92487 5.92499 1 12.0001 1C18.0753 1 23.0001 5.92487 23.0001 12C23.0001 18.0751 18.0753 23 12.0001 23C5.92499 23 1.00012 18.0751 1.00012 12ZM13.0001 6.5V14H11.0001V6.5H13.0001ZM11.0001 15.5H13.004V17.5039H11.0001V15.5Z" fill="#8b8b8b" />
</svg>

After

Width:  |  Height:  |  Size: 532 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 32 32"><path fill="#20744a" fill-rule="evenodd" d="M28.781 4.405h-10.13V2.018L2 4.588v22.527l16.651 2.868v-3.538h10.13A1.16 1.16 0 0 0 30 25.349V5.5a1.16 1.16 0 0 0-1.219-1.095m.16 21.126H18.617l-.017-1.889h2.487v-2.2h-2.506l-.012-1.3h2.518v-2.2H18.55l-.012-1.3h2.549v-2.2H18.53v-1.3h2.557v-2.2H18.53v-1.3h2.557v-2.2H18.53v-2h10.411Z"/><path fill="#20744a" d="M22.487 7.439h4.323v2.2h-4.323zm0 3.501h4.323v2.2h-4.323zm0 3.501h4.323v2.2h-4.323zm0 3.501h4.323v2.2h-4.323zm0 3.501h4.323v2.2h-4.323z"/><path fill="#fff" fill-rule="evenodd" d="m6.347 10.673l2.146-.123l1.349 3.709l1.594-3.862l2.146-.123l-2.606 5.266l2.606 5.279l-2.269-.153l-1.532-4.024l-1.533 3.871l-2.085-.184l2.422-4.663z"/></svg>

After

Width:  |  Height:  |  Size: 773 B

View File

@ -0,0 +1,3 @@
<svg width="20px" height="20px" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4 4C4 2.89543 4.89543 2 6 2H10.5858C11.1162 2 11.6249 2.21071 12 2.58579L15.4142 6C15.7893 6.37507 16 6.88378 16 7.41421V16C16 17.1046 15.1046 18 14 18H6C4.89543 18 4 17.1046 4 16V4ZM6 10C6 9.44772 6.44772 9 7 9H13C13.5523 9 14 9.44772 14 10C14 10.5523 13.5523 11 13 11H7C6.44772 11 6 10.5523 6 10ZM7 13C6.44772 13 6 13.4477 6 14C6 14.5523 6.44772 15 7 15H13C13.5523 15 14 14.5523 14 14C14 13.4477 13.5523 13 13 13H7Z" fill="#3895FB"/>
</svg>

After

Width:  |  Height:  |  Size: 598 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 16 16"><g fill="none"><path fill="url(#fluentColorImage162)" d="M2 4.5A2.5 2.5 0 0 1 4.5 2h7A2.5 2.5 0 0 1 14 4.5v7a2.5 2.5 0 0 1-2.5 2.5h-7A2.5 2.5 0 0 1 2 11.5z"/><path fill="url(#fluentColorImage160)" d="M13.586 12.879A2.5 2.5 0 0 1 11.5 14h-7a2.5 2.5 0 0 1-2.086-1.121l4.384-4.384a1.7 1.7 0 0 1 2.404 0z"/><path fill="url(#fluentColorImage161)" d="M11.5 5.502a1.002 1.002 0 1 1-2.004 0a1.002 1.002 0 0 1 2.004 0"/><defs><linearGradient id="fluentColorImage160" x1="6.286" x2="7.572" y1="7.997" y2="14.347" gradientUnits="userSpaceOnUse"><stop stop-color="#b3e0ff"/><stop offset="1" stop-color="#8cd0ff"/></linearGradient><linearGradient id="fluentColorImage161" x1="10.097" x2="10.829" y1="4.277" y2="6.913" gradientUnits="userSpaceOnUse"><stop stop-color="#fdfdfd"/><stop offset="1" stop-color="#b3e0ff"/></linearGradient><radialGradient id="fluentColorImage162" cx="0" cy="0" r="1" gradientTransform="matrix(20.57146 26.03575 -23.68122 18.71109 -2.714 -4.75)" gradientUnits="userSpaceOnUse"><stop offset=".338" stop-color="#0fafff"/><stop offset=".529" stop-color="#367af2"/></radialGradient></defs></g></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M2.00003 4V6H22V4H2.00003Z" fill="black" /><path d="M8.00003 11V13H22V11H8.00003Z" fill="black" /><path d="M2.00003 18H22V20H2.00003V18Z" fill="black" /><path d="M1.58582 11.9998L4.7678 15.1818L6.18201 13.7676L4.41424 11.9998L6.18201 10.232L4.7678 8.81783L1.58582 11.9998Z" fill="rgb(95, 114, 146)" />
</svg>

After

Width:  |  Height:  |  Size: 415 B

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M2.99997 4H1.99997V6H2.99997H21H22V4H21H2.99997ZM8.99997 11H7.99997V13H8.99997H21H22V11H21H8.99997ZM1.99997 18H2.99997H21H22V20H21H2.99997H1.99997V18ZM5.80474 12.7073L6.51184 12.0002L5.80474 11.2931L4.03697 9.52532L3.32986 8.81821L1.91565 10.2324L2.62276 10.9395L3.68342 12.0002L2.62276 13.0608L1.91565 13.768L3.32986 15.1822L4.03697 14.4751L5.80474 12.7073Z" fill="rgb(95, 114, 146)" />
</svg>

After

Width:  |  Height:  |  Size: 501 B

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M3.05493 11C3.45261 7.40254 5.97062 4.44413 9.33156 3.40217C8.00851 5.65483 7.1892 8.23957 7.02895 11H3.05493ZM10.9922 1.04555C5.38944 1.55442 1 6.26461 1 12C1 17.7354 5.3894 22.4455 10.9922 22.9545L11 22.9643L11.4254 22.9853C11.4932 22.9887 11.5611 22.9916 11.6292 22.9939C11.7523 22.9979 11.8759 23 12 23C12.1187 23 12.2369 22.9981 12.3546 22.9944C12.4281 22.9921 12.5015 22.989 12.5746 22.9853L13 22.9643L13.0078 22.9545C18.6106 22.4455 23 17.7354 23 12C23 6.26461 18.6106 1.55443 13.0078 1.04555L12.9999 1.03571L12.5736 1.0147C12.4767 1.00972 12.3795 1.006 12.2819 1.00354C12.1882 1.00119 12.0942 1 12 1C11.9051 1 11.8104 1.0012 11.7161 1.00359C11.6192 1.00605 11.5226 1.00976 11.4263 1.0147L11.0001 1.03571L10.9922 1.04555ZM12.0011 3C13.6972 5.25767 14.7704 8.00828 14.9672 11H9.03278C9.22955 8.00828 10.3028 5.25767 11.9989 3C11.9992 3 11.9996 3 12 3C12.0004 3 12.0008 3 12.0011 3ZM7.02894 13C7.18917 15.7604 8.00847 18.3452 9.3315 20.5978C5.97059 19.5558 3.45261 16.5974 3.05493 13H7.02894ZM11.9988 21C10.3028 18.7423 9.22953 15.9917 9.03277 13H14.9672C14.7705 15.9917 13.6972 18.7423 12.0012 21C12.0008 21 12.0004 21 12 21C11.9996 21 11.9992 21 11.9988 21ZM14.6685 20.5978C15.9915 18.3452 16.8108 15.7604 16.9711 13H20.9451C20.5474 16.5974 18.0294 19.5558 14.6685 20.5978ZM16.9711 11C16.8108 8.23957 15.9915 5.65483 14.6684 3.40217C18.0294 4.44413 20.5474 7.40254 20.9451 11H16.9711Z" fill="rgb(95, 114, 146)" />
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="rgb(77, 107, 254)" xmlns="http://www.w3.org/2000/svg"><path d="M3.05493 11C3.45261 7.40254 5.97062 4.44413 9.33156 3.40217C8.00851 5.65483 7.1892 8.23957 7.02895 11H3.05493ZM10.9922 1.04555C5.38944 1.55442 1 6.26461 1 12C1 17.7354 5.3894 22.4455 10.9922 22.9545L11 22.9643L11.4254 22.9853C11.4932 22.9887 11.5611 22.9916 11.6292 22.9939C11.7523 22.9979 11.8759 23 12 23C12.1187 23 12.2369 22.9981 12.3546 22.9944C12.4281 22.9921 12.5015 22.989 12.5746 22.9853L13 22.9643L13.0078 22.9545C18.6106 22.4455 23 17.7354 23 12C23 6.26461 18.6106 1.55443 13.0078 1.04555L12.9999 1.03571L12.5736 1.0147C12.4767 1.00972 12.3795 1.006 12.2819 1.00354C12.1882 1.00119 12.0942 1 12 1C11.9051 1 11.8104 1.0012 11.7161 1.00359C11.6192 1.00605 11.5226 1.00976 11.4263 1.0147L11.0001 1.03571L10.9922 1.04555ZM12.0011 3C13.6972 5.25767 14.7704 8.00828 14.9672 11H9.03278C9.22955 8.00828 10.3028 5.25767 11.9989 3C11.9992 3 11.9996 3 12 3C12.0004 3 12.0008 3 12.0011 3ZM7.02894 13C7.18917 15.7604 8.00847 18.3452 9.3315 20.5978C5.97059 19.5558 3.45261 16.5974 3.05493 13H7.02894ZM11.9988 21C10.3028 18.7423 9.22953 15.9917 9.03277 13H14.9672C14.7705 15.9917 13.6972 18.7423 12.0012 21C12.0008 21 12.0004 21 12 21C11.9996 21 11.9992 21 11.9988 21ZM14.6685 20.5978C15.9915 18.3452 16.8108 15.7604 16.9711 13H20.9451C20.5474 16.5974 18.0294 19.5558 14.6685 20.5978ZM16.9711 11C16.8108 8.23957 15.9915 5.65483 14.6684 3.40217C18.0294 4.44413 20.5474 7.40254 20.9451 11H16.9711Z" fill="rgb(77, 107, 254)" />
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 3H23V21H1V3ZM3 5V19H21V5H3ZM4.99609 7.5H7V9.50391H4.99609V7.5ZM8.99609 7.5H11V9.50391H8.99609V7.5ZM12.9961 7.5H15V9.50391H12.9961V7.5ZM16.9961 7.5H19V9.50391H16.9961V7.5ZM4.99609 10.5H7V12.5039H4.99609V10.5ZM8.99609 10.5H11V12.5039H8.99609V10.5ZM12.9961 10.5H15V12.5039H12.9961V10.5ZM16.9961 10.5H19V12.5039H16.9961V10.5ZM5 15H19V17H5V15Z" fill="black" />
</svg>

After

Width:  |  Height:  |  Size: 472 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><path fill="rgb(95, 114, 146)" d="M12,4a8,8,0,0,1,7.89,6.7A1.53,1.53,0,0,0,21.38,12h0a1.5,1.5,0,0,0,1.48-1.75,11,11,0,0,0-21.72,0A1.5,1.5,0,0,0,2.62,12h0a1.53,1.53,0,0,0,1.49-1.3A8,8,0,0,1,12,4Z"><animateTransform attributeName="transform" dur="0.75s" repeatCount="indefinite" type="rotate" values="0 12 12;360 12 12"/></path></svg>

After

Width:  |  Height:  |  Size: 417 B

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3ZM1 12C1 5.92487 5.92487 1 12 1C18.0751 1 23 5.92487 23 12C23 18.0751 18.0751 23 12 23C5.92487 23 1 18.0751 1 12ZM10.5 7V17H8.5V7H10.5ZM15.5 7V17H13.5V7H15.5Z" fill="#8b8b8b" />
</svg>

After

Width:  |  Height:  |  Size: 402 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="192" height="192" viewBox="0 0 32 32"><path fill="#909090" d="m24.1 2.072l5.564 5.8v22.056H8.879V30h20.856V7.945z"/><path fill="#f4f4f4" d="M24.031 2H8.808v27.928h20.856V7.873z"/><path fill="#7a7b7c" d="M8.655 3.5h-6.39v6.827h20.1V3.5z"/><path fill="#dd2025" d="M22.472 10.211H2.395V3.379h20.077z"/><path fill="#464648" d="M9.052 4.534H7.745v4.8h1.028V7.715L9 7.728a2 2 0 0 0 .647-.117a1.4 1.4 0 0 0 .493-.291a1.2 1.2 0 0 0 .335-.454a2.1 2.1 0 0 0 .105-.908a2.2 2.2 0 0 0-.114-.644a1.17 1.17 0 0 0-.687-.65a2 2 0 0 0-.409-.104a2 2 0 0 0-.319-.026m-.189 2.294h-.089v-1.48h.193a.57.57 0 0 1 .459.181a.92.92 0 0 1 .183.558c0 .246 0 .469-.222.626a.94.94 0 0 1-.524.114m3.671-2.306c-.111 0-.219.008-.295.011L12 4.538h-.78v4.8h.918a2.7 2.7 0 0 0 1.028-.175a1.7 1.7 0 0 0 .68-.491a1.9 1.9 0 0 0 .373-.749a3.7 3.7 0 0 0 .114-.949a4.4 4.4 0 0 0-.087-1.127a1.8 1.8 0 0 0-.4-.733a1.6 1.6 0 0 0-.535-.4a2.4 2.4 0 0 0-.549-.178a1.3 1.3 0 0 0-.228-.017m-.182 3.937h-.1V5.392h.013a1.06 1.06 0 0 1 .6.107a1.2 1.2 0 0 1 .324.4a1.3 1.3 0 0 1 .142.526c.009.22 0 .4 0 .549a3 3 0 0 1-.033.513a1.8 1.8 0 0 1-.169.5a1.1 1.1 0 0 1-.363.36a.67.67 0 0 1-.416.106m5.08-3.915H15v4.8h1.028V7.434h1.3v-.892h-1.3V5.43h1.4v-.892"/><path fill="#dd2025" d="M21.781 20.255s3.188-.578 3.188.511s-1.975.646-3.188-.511m-2.357.083a7.5 7.5 0 0 0-1.473.489l.4-.9c.4-.9.815-2.127.815-2.127a14 14 0 0 0 1.658 2.252a13 13 0 0 0-1.4.288Zm-1.262-6.5c0-.949.307-1.208.546-1.208s.508.115.517.939a10.8 10.8 0 0 1-.517 2.434a4.4 4.4 0 0 1-.547-2.162Zm-4.649 10.516c-.978-.585 2.051-2.386 2.6-2.444c-.003.001-1.576 3.056-2.6 2.444M25.9 20.895c-.01-.1-.1-1.207-2.07-1.16a14 14 0 0 0-2.453.173a12.5 12.5 0 0 1-2.012-2.655a11.8 11.8 0 0 0 .623-3.1c-.029-1.2-.316-1.888-1.236-1.878s-1.054.815-.933 2.013a9.3 9.3 0 0 0 .665 2.338s-.425 1.323-.987 2.639s-.946 2.006-.946 2.006a9.6 9.6 0 0 0-2.725 1.4c-.824.767-1.159 1.356-.725 1.945c.374.508 1.683.623 2.853-.91a23 23 0 0 0 1.7-2.492s1.784-.489 2.339-.623s1.226-.24 1.226-.24s1.629 1.639 3.2 1.581s1.495-.939 1.485-1.035"/><path fill="#909090" d="M23.954 2.077V7.95h5.633z"/><path fill="#f4f4f4" d="M24.031 2v5.873h5.633z"/><path fill="#fff" d="M8.975 4.457H7.668v4.8H8.7V7.639l.228.013a2 2 0 0 0 .647-.117a1.4 1.4 0 0 0 .493-.291a1.2 1.2 0 0 0 .332-.454a2.1 2.1 0 0 0 .105-.908a2.2 2.2 0 0 0-.114-.644a1.17 1.17 0 0 0-.687-.65a2 2 0 0 0-.411-.105a2 2 0 0 0-.319-.026m-.189 2.294h-.089v-1.48h.194a.57.57 0 0 1 .459.181a.92.92 0 0 1 .183.558c0 .246 0 .469-.222.626a.94.94 0 0 1-.524.114m3.67-2.306c-.111 0-.219.008-.295.011l-.235.006h-.78v4.8h.918a2.7 2.7 0 0 0 1.028-.175a1.7 1.7 0 0 0 .68-.491a1.9 1.9 0 0 0 .373-.749a3.7 3.7 0 0 0 .114-.949a4.4 4.4 0 0 0-.087-1.127a1.8 1.8 0 0 0-.4-.733a1.6 1.6 0 0 0-.535-.4a2.4 2.4 0 0 0-.549-.178a1.3 1.3 0 0 0-.228-.017m-.182 3.937h-.1V5.315h.013a1.06 1.06 0 0 1 .6.107a1.2 1.2 0 0 1 .324.4a1.3 1.3 0 0 1 .142.526c.009.22 0 .4 0 .549a3 3 0 0 1-.033.513a1.8 1.8 0 0 1-.169.5a1.1 1.1 0 0 1-.363.36a.67.67 0 0 1-.416.106m5.077-3.915h-2.43v4.8h1.028V7.357h1.3v-.892h-1.3V5.353h1.4v-.892"/></svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g opacity="0.9"> <path d="M12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3ZM1 12C1 5.92487 5.92487 1 12 1C18.0751 1 23 5.92487 23 12C23 18.0751 18.0751 23 12 23C5.92487 23 1 18.0751 1 12ZM8.5 6.37083L18.25 12L8.5 17.6292L8.5 6.37083ZM10.5 9.83494L10.5 14.1651L14.25 12L10.5 9.83494Z" fill="#8b8b8b" /></g>
</svg>

After

Width:  |  Height:  |  Size: 485 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><rect width="2.8" height="12" x="1" y="6" fill="#8b8b8b"><animate id="svgSpinnersBarsScale0" attributeName="y" begin="0;svgSpinnersBarsScale1.end-0.1s" calcMode="spline" dur="0.6s" keySplines=".36,.61,.3,.98;.36,.61,.3,.98" values="6;1;6"/><animate attributeName="height" begin="0;svgSpinnersBarsScale1.end-0.1s" calcMode="spline" dur="0.6s" keySplines=".36,.61,.3,.98;.36,.61,.3,.98" values="12;22;12"/></rect><rect width="2.8" height="12" x="5.8" y="6" fill="#8b8b8b"><animate attributeName="y" begin="svgSpinnersBarsScale0.begin+0.1s" calcMode="spline" dur="0.6s" keySplines=".36,.61,.3,.98;.36,.61,.3,.98" values="6;1;6"/><animate attributeName="height" begin="svgSpinnersBarsScale0.begin+0.1s" calcMode="spline" dur="0.6s" keySplines=".36,.61,.3,.98;.36,.61,.3,.98" values="12;22;12"/></rect><rect width="2.8" height="12" x="10.6" y="6" fill="#8b8b8b"><animate attributeName="y" begin="svgSpinnersBarsScale0.begin+0.2s" calcMode="spline" dur="0.6s" keySplines=".36,.61,.3,.98;.36,.61,.3,.98" values="6;1;6"/><animate attributeName="height" begin="svgSpinnersBarsScale0.begin+0.2s" calcMode="spline" dur="0.6s" keySplines=".36,.61,.3,.98;.36,.61,.3,.98" values="12;22;12"/></rect><rect width="2.8" height="12" x="15.4" y="6" fill="#8b8b8b"><animate attributeName="y" begin="svgSpinnersBarsScale0.begin+0.3s" calcMode="spline" dur="0.6s" keySplines=".36,.61,.3,.98;.36,.61,.3,.98" values="6;1;6"/><animate attributeName="height" begin="svgSpinnersBarsScale0.begin+0.3s" calcMode="spline" dur="0.6s" keySplines=".36,.61,.3,.98;.36,.61,.3,.98" values="12;22;12"/></rect><rect width="2.8" height="12" x="20.2" y="6" fill="#8b8b8b"><animate id="svgSpinnersBarsScale1" attributeName="y" begin="svgSpinnersBarsScale0.begin+0.4s" calcMode="spline" dur="0.6s" keySplines=".36,.61,.3,.98;.36,.61,.3,.98" values="6;1;6"/><animate attributeName="height" begin="svgSpinnersBarsScale0.begin+0.4s" calcMode="spline" dur="0.6s" keySplines=".36,.61,.3,.98;.36,.61,.3,.98" values="12;22;12"/></rect></svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 32 32"><path fill="#d33922" d="M18.536 2.321v2.863c3.4.019 7.357-.035 10.754.016c.642 0 .67.568.678 1.064c.054 5.942-.013 12.055.032 18c-.012.234-.006 1.1-.013 1.346c-.022.823-.434.859-1.257.884c-.132 0-.52.006-.648.012c-3.181-.016-6.362-.009-9.546-.009v3.182L2 27.134V4.873z"/><path fill="#fff" d="M18.536 6.138h10.5v19.4h-10.5V23h7.634v-1.275h-7.634v-1.59h7.634v-1.272h-7.631q.002-.936-.006-1.87a4.47 4.47 0 0 0 3.82-.375a4.35 4.35 0 0 0 1.959-3.474c-1.4-.01-2.793-.006-4.186-.006c0-1.384.016-2.767-.029-4.148c-.522.1-1.043.21-1.562.321V6.139"/><path fill="#d33922" d="M20.766 8.324a4.476 4.476 0 0 1 4.186 4.167c-1.4.016-2.793.01-4.189.01V8.324"/><path fill="#fff" d="M7.1 10.726c1.727.083 3.82-.684 5.252.611c1.371 1.664 1.008 4.724-1.024 5.719A4.7 4.7 0 0 1 9 17.348c0 1.244-.006 2.488 0 3.731q-.947-.082-1.893-.159c-.029-3.4-.035-6.8 0-10.2"/><path fill="#d33922" d="M8.993 12.446c.627-.029 1.4-.143 1.826.445a2.3 2.3 0 0 1 .041 2.087c-.363.655-1.183.592-1.816.668c-.067-1.066-.06-2.131-.051-3.2"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M15.0961 5.90381C12.5577 3.3654 8.44209 3.3654 5.90368 5.90381C3.36528 8.44221 3.36528 12.5578 5.90368 15.0962C8.44209 17.6346 12.5577 17.6346 15.0961 15.0962C17.6345 12.5578 17.6345 8.44221 15.0961 5.90381ZM4.48947 4.48959C7.80893 1.17014 13.1908 1.17014 16.5103 4.48959C19.5905 7.56982 19.8125 12.4259 17.1762 15.7621L22.5207 21.1066L21.1065 22.5208L15.762 17.1764C12.4258 19.8126 7.5697 19.5906 4.48947 16.5104C1.17001 13.191 1.17001 7.80905 4.48947 4.48959Z" fill="rgb(95, 114, 146)" />
</svg>

After

Width:  |  Height:  |  Size: 604 B

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_100396_60187)"> <path d="M0.291748 1.66449L24.0026 12L0.291748 22.3355L3.93958 12L0.291748 1.66449ZM5.70755 13L3.70832 18.6645L18.9974 12L3.70832 5.33551L5.70755 11H11V13H5.70755Z" fill="#fff" /></g><defs> <clipPath id="clip0_100396_60187"> <rect width="24" height="24" fill="white" /> </clipPath></defs>
</svg>

After

Width:  |  Height:  |  Size: 458 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><rect width="2.8" height="12" x="1" y="6" fill="#68c692"><animate attributeName="y" begin="svgSpinnersBarsScaleMiddle0.begin+0.4s" calcMode="spline" dur="0.6s" keySplines=".14,.73,.34,1;.65,.26,.82,.45" values="6;1;6"/><animate attributeName="height" begin="svgSpinnersBarsScaleMiddle0.begin+0.4s" calcMode="spline" dur="0.6s" keySplines=".14,.73,.34,1;.65,.26,.82,.45" values="12;22;12"/></rect><rect width="2.8" height="12" x="5.8" y="6" fill="#68c692"><animate attributeName="y" begin="svgSpinnersBarsScaleMiddle0.begin+0.2s" calcMode="spline" dur="0.6s" keySplines=".14,.73,.34,1;.65,.26,.82,.45" values="6;1;6"/><animate attributeName="height" begin="svgSpinnersBarsScaleMiddle0.begin+0.2s" calcMode="spline" dur="0.6s" keySplines=".14,.73,.34,1;.65,.26,.82,.45" values="12;22;12"/></rect><rect width="2.8" height="12" x="10.6" y="6" fill="#68c692"><animate id="svgSpinnersBarsScaleMiddle0" attributeName="y" begin="0;svgSpinnersBarsScaleMiddle1.end-0.1s" calcMode="spline" dur="0.6s" keySplines=".14,.73,.34,1;.65,.26,.82,.45" values="6;1;6"/><animate attributeName="height" begin="0;svgSpinnersBarsScaleMiddle1.end-0.1s" calcMode="spline" dur="0.6s" keySplines=".14,.73,.34,1;.65,.26,.82,.45" values="12;22;12"/></rect><rect width="2.8" height="12" x="15.4" y="6" fill="#68c692"><animate attributeName="y" begin="svgSpinnersBarsScaleMiddle0.begin+0.2s" calcMode="spline" dur="0.6s" keySplines=".14,.73,.34,1;.65,.26,.82,.45" values="6;1;6"/><animate attributeName="height" begin="svgSpinnersBarsScaleMiddle0.begin+0.2s" calcMode="spline" dur="0.6s" keySplines=".14,.73,.34,1;.65,.26,.82,.45" values="12;22;12"/></rect><rect width="2.8" height="12" x="20.2" y="6" fill="#68c692"><animate id="svgSpinnersBarsScaleMiddle1" attributeName="y" begin="svgSpinnersBarsScaleMiddle0.begin+0.4s" calcMode="spline" dur="0.6s" keySplines=".14,.73,.34,1;.65,.26,.82,.45" values="6;1;6"/><animate attributeName="height" begin="svgSpinnersBarsScaleMiddle0.begin+0.4s" calcMode="spline" dur="0.6s" keySplines=".14,.73,.34,1;.65,.26,.82,.45" values="12;22;12"/></rect></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12.0001 3.00488C7.02956 3.00488 3.00012 7.03432 3.00012 12.0049C3.00012 16.9754 7.02956 21.0049 12.0001 21.0049C16.9707 21.0049 21.0001 16.9754 21.0001 12.0049C21.0001 7.03432 16.9707 3.00488 12.0001 3.00488ZM1.00012 12.0049C1.00012 5.92975 5.92499 1.00488 12.0001 1.00488C18.0753 1.00488 23.0001 5.92975 23.0001 12.0049C23.0001 18.08 18.0753 23.0049 12.0001 23.0049C5.92499 23.0049 1.00012 18.08 1.00012 12.0049ZM13.0001 6.50488L13.0001 11.0049H17.5001V13.0049H13.0001L13.0001 17.5049H11.0001L11.0001 13.0049H6.50012V11.0049H11.0001L11.0001 6.50488H13.0001Z" fill="#333" />
</svg>

After

Width:  |  Height:  |  Size: 689 B

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11.5001 1.85059L23.5526 12L11.5001 22.1495V15.9233C7.30565 16.1238 4.4268 17.6231 2.31385 20.5813L0.519531 19.8039C1.04297 17.1867 2.10079 14.5092 3.99754 12.3415C5.75937 10.328 8.20695 8.79947 11.5001 8.15506L11.5001 1.85059ZM13.5001 6.14948L13.5001 9.86733L12.6415 9.98998C9.37204 10.4571 7.09103 11.8433 5.50269 13.6585C4.79919 14.4625 4.22297 15.3609 3.75752 16.3174C6.09576 14.6658 8.97868 13.9 12.5001 13.9H13.5001V17.8506L20.4476 12L13.5001 6.14948Z" fill="#8b8b8b" />
</svg>

After

Width:  |  Height:  |  Size: 590 B

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M15.0001 1.29053V22.71L5.73703 17.5003L1.00013 17.5003L1.00012 6.50025H5.73703L15.0001 1.29053ZM4.99894 8.50025H3.00012L3.00012 15.5003L4.99894 15.5003L4.99894 8.50025ZM6.99894 15.9154L13.0001 19.2905V4.70998L6.99894 8.08514V15.9154ZM20.9793 6.98204L21.5819 7.78005C22.5 8.99571 22.9964 10.4777 22.9959 12.001C22.9953 13.5244 22.4979 15.006 21.579 16.2211L20.9758 17.0186L19.3806 15.8122L19.9838 15.0146C20.6402 14.1468 20.9955 13.0885 20.9959 12.0004C20.9963 10.9122 20.6417 9.85368 19.9859 8.98535L19.3832 8.18734L20.9793 6.98204ZM18.1865 9.09117L18.7892 9.88918C19.2482 10.497 19.4964 11.238 19.4962 11.9997C19.4959 12.7614 19.2472 13.5022 18.7877 14.1097L18.1845 14.9073L16.5893 13.7009L17.1925 12.9033C17.3895 12.6429 17.4961 12.3254 17.4962 11.999C17.4963 11.6725 17.3899 11.355 17.1932 11.0945L16.5905 10.2965L18.1865 9.09117Z" fill="#8b8b8b" />
</svg>

After

Width:  |  Height:  |  Size: 966 B

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3ZM1 12C1 5.92487 5.92487 1 12 1C18.0751 1 23 5.92487 23 12C23 18.0751 18.0751 23 12 23C5.92487 23 1 18.0751 1 12Z" fill="#d54941" /><path d="M8 8H16V16H8V8Z" fill="#d54941" />
</svg>

After

Width:  |  Height:  |  Size: 400 B

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M13.5266 5.69361C14.3427 6.35808 15.1567 7.09207 15.9514 7.88684C16.746 8.68139 17.4798 9.49511 18.1441 10.3111C18.7149 9.43842 19.1779 8.59599 19.5225 7.81267C19.9773 6.77901 20.2042 5.89778 20.2328 5.21464C20.2615 4.53265 20.093 4.1556 19.8875 3.95017C19.6821 3.74474 19.305 3.57623 18.6231 3.60485C17.9399 3.63353 17.0587 3.86037 16.025 4.31518C15.2417 4.65985 14.3992 5.12284 13.5266 5.69361ZM11.8389 4.41997C13.0019 3.61089 14.1454 2.95715 15.2195 2.48455C16.3866 1.97103 17.5214 1.64933 18.5392 1.60661C19.5581 1.56385 20.5669 1.80109 21.3017 2.53596C22.0366 3.27082 22.2738 4.27958 22.2311 5.29851C22.1884 6.31628 21.8667 7.45107 21.3531 8.61814C20.8806 9.69221 20.2269 10.8357 19.4178 11.9986C20.2271 13.1619 20.881 14.3056 21.3537 15.38C21.8672 16.547 22.1889 17.6818 22.2317 18.6996C22.2744 19.7185 22.0372 20.7273 21.3023 21.4621C20.5675 22.197 19.5587 22.4342 18.5398 22.3915C17.522 22.3488 16.3872 22.0271 15.2201 21.5136C14.1459 21.0409 13.0021 20.387 11.8389 19.5777C10.6758 20.387 9.53203 21.0409 8.45774 21.5136C7.29067 22.0271 6.15588 22.3488 5.13811 22.3915C4.11918 22.4342 3.11042 22.197 2.37556 21.4621C1.64069 20.7273 1.40345 19.7185 1.44621 18.6996C1.48893 17.6818 1.81063 16.547 2.32414 15.38C2.79685 14.3056 3.45076 13.1619 4.26006 11.9986C3.45103 10.8357 2.79732 9.69221 2.32473 8.61814C1.81122 7.45107 1.48952 6.31628 1.4468 5.29851C1.40403 4.27958 1.64128 3.27082 2.37614 2.53596C3.11101 1.80109 4.11976 1.56385 5.13869 1.60661C6.15647 1.64933 7.29126 1.97103 8.45833 2.48455C9.53243 2.95715 10.676 3.61089 11.8389 4.41997ZM5.53375 10.3111C6.19807 9.49511 6.93189 8.68139 7.72644 7.88684C8.5212 7.09207 9.33514 6.35808 10.1513 5.69361C9.27865 5.12284 8.43619 4.65985 7.65285 4.31518C6.6192 3.86037 5.73796 3.63353 5.05482 3.60485C4.37284 3.57623 3.99578 3.74474 3.79036 3.95017C3.58493 4.1556 3.41641 4.53265 3.44504 5.21464C3.47371 5.89778 3.70055 6.77901 4.15536 7.81267C4.50002 8.59599 4.963 9.43842 5.53375 10.3111ZM6.74564 11.9987C7.44705 12.8959 8.24907 13.8049 9.14123 14.697C10.0332 15.589 10.942 16.3908 11.8389 17.0921C12.7359 16.3908 13.6447 15.589 14.5366 14.697C15.4288 13.8049 16.2308 12.8959 16.9322 11.9987C16.231 11.1017 15.4291 10.193 14.5372 9.30105C13.6451 8.40891 12.7361 7.60691 11.8389 6.90552C10.9418 7.60691 10.0328 8.40891 9.14065 9.30105C8.24873 10.193 7.44691 11.1017 6.74564 11.9987ZM5.53368 13.6862C4.9627 14.5592 4.49954 15.4019 4.15477 16.1854C3.69997 17.2191 3.47313 18.1003 3.44445 18.7835C3.41583 19.4654 3.58434 19.8425 3.78977 20.0479C3.9952 20.2534 4.37225 20.4219 5.05424 20.3932C5.73738 20.3646 6.61861 20.1377 7.65227 19.6829C8.4358 19.3382 9.27848 18.875 10.1514 18.3041C9.33538 17.6397 8.52161 16.9059 7.72702 16.1113C6.93221 15.3165 6.19818 14.5025 5.53368 13.6862ZM13.5265 18.3041C14.3994 18.875 15.2421 19.3382 16.0256 19.6829C17.0593 20.1377 17.9405 20.3646 18.6236 20.3932C19.3056 20.4219 19.6827 20.2534 19.8881 20.0479C20.0935 19.8425 20.2621 19.4654 20.2334 18.7835C20.2048 18.1003 19.9779 17.2191 19.5231 16.1854C19.1783 15.4019 18.7152 14.5592 18.1442 13.6862C17.4797 14.5025 16.7457 15.3165 15.9509 16.1113C15.1563 16.9059 14.3425 17.6397 13.5265 18.3041ZM11.0001 11.0001H13.004V13.004H11.0001V11.0001Z" fill="rgb(95, 114, 146)" />
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10.8786 21.9999L12.1576 21.7868C14.0863 21.4653 15.5 19.7965 15.5 17.8412V15.3826H20.8195C22.0554 15.3826 22.9955 14.2728 22.7923 13.0538L21.1257 3.05377C20.9649 2.08939 20.1306 1.38257 19.1529 1.38257H7V12.5804L10.8786 21.9999ZM12.1131 19.7451L9 12.1847V3.38257H19.1529L20.8195 13.3826H13.5V17.8412C13.5 18.72 12.9289 19.483 12.1131 19.7451ZM4 13.3826V1.38257H2V13.3826H4Z" fill="#8b8b8b" />
</svg>

After

Width:  |  Height:  |  Size: 507 B

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10.8786 1.38257L12.1576 1.59573C14.0863 1.91719 15.5 3.58595 15.5 5.54131V7.99992H20.8195C22.0554 7.99992 22.9955 9.10966 22.7923 10.3287L21.1257 20.3287C20.9649 21.2931 20.1306 21.9999 19.1529 21.9999H7V10.8021L10.8786 1.38257ZM12.1131 3.63742L9 11.1977V19.9999L19.1529 19.9999L20.8195 9.99992H13.5V5.54131C13.5 4.66252 12.9289 3.89951 12.1131 3.63742ZM4 9.99992V21.9999H2V9.99992H4Z" fill="#8b8b8b" />
</svg>

After

Width:  |  Height:  |  Size: 518 B

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11.0001 4.5L11.0002 16.0858L6.50015 11.5858L5.08594 13L12.0002 19.9142L18.9144 13L17.5001 11.5858L13.0002 16.0858L13.0001 4.5L11.0001 4.5Z" fill="rgb(128, 128, 128)" />
</svg>

After

Width:  |  Height:  |  Size: 281 B

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1.99988 2H21.9999V22H1.99988V2ZM3.99988 20H17.5857L8.99988 11.4142L3.99988 16.4142V20ZM19.9999 19.5858V4H3.99988V13.5858L8.99988 8.58579L19.9999 19.5858ZM15.7968 7.25C15.2445 7.25 14.7968 7.69772 14.7968 8.25C14.7968 8.80228 15.2445 9.25 15.7968 9.25C16.349 9.25 16.7968 8.80228 16.7968 8.25C16.7968 7.69772 16.349 7.25 15.7968 7.25ZM12.7968 8.25C12.7968 6.59315 14.1399 5.25 15.7968 5.25C17.4536 5.25 18.7968 6.59315 18.7968 8.25C18.7968 9.90685 17.4536 11.25 15.7968 11.25C14.1399 11.25 12.7968 9.90685 12.7968 8.25Z" fill="rgb(95, 114, 146)" />
</svg>

After

Width:  |  Height:  |  Size: 662 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 48 48"><g fill="none"><path stroke="#000" stroke-linejoin="round" stroke-width="4" d="M24 44c11.046 0 20-8.954 20-20S35.046 4 24 4S4 12.954 4 24s8.954 20 20 20Z"/><path fill="#000" d="M17 25.9a2 2 0 1 0 0-4a2 2 0 0 0 0 4"/><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M21.95 28.85A6.98 6.98 0 0 0 24 23.9a6.98 6.98 0 0 0-2.05-4.95m4.95 14.849a13.96 13.96 0 0 0 4.1-9.9c0-3.866-1.567-7.366-4.1-9.899"/></g></svg>

After

Width:  |  Height:  |  Size: 531 B

View File

@ -0,0 +1,2 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M8.75 4.25C4.85689 4.25 2 6.88401 2 9.79408C2 11.3943 2.83472 12.8796 4.25197 13.9231C4.54488 14.1387 4.69759 14.4964 4.65074 14.8571L4.58136 15.3913L5.41173 14.9745C5.65098 14.8543 5.9284 14.8352 6.1819 14.9212C6.97088 15.1891 7.8375 15.3382 8.75 15.3382C9.00377 15.3382 9.254 15.3266 9.5 15.3042C9.50387 12.0935 12.1736 9.6258 15.4462 9.10286C15.0367 6.47982 12.332 4.25 8.75 4.25ZM17.4568 9.03026C21.0029 9.33461 24 11.9035 24 15.3112C24 16.9476 23.2887 18.413 22.1671 19.5096V21.4591C22.1671 21.8163 21.9765 22.1465 21.6671 22.3251C21.3577 22.5037 20.9765 22.5037 20.6671 22.3251L18.9424 21.3293C18.2488 21.5201 17.5119 21.6224 16.75 21.6224C13.6277 21.6224 10.8262 19.8749 9.85702 17.2784C9.49411 17.3179 9.12461 17.3382 8.75 17.3382C7.77222 17.3382 6.82917 17.2 5.94709 16.9436L3.79583 18.0236C3.4652 18.1896 3.06995 18.1595 2.76828 17.9454C2.46661 17.7312 2.30782 17.368 2.35547 17.0011L2.59463 15.1598C1.0185 13.8152 0 11.9258 0 9.79408C0 5.47576 4.0827 2.25 8.75 2.25C13.1377 2.25 17.0088 5.10087 17.4568 9.03026ZM5.24609 7.49609C5.24609 6.94381 5.69381 6.49609 6.24609 6.49609H6.25C6.80228 6.49609 7.25 6.94381 7.25 7.49609V7.5C7.25 8.05228 6.80228 8.5 6.25 8.5H6.24609C5.69381 8.5 5.24609 8.05228 5.24609 7.5V7.49609ZM10.25 7.50732C10.25 6.95504 10.6977 6.50732 11.25 6.50732H11.2539C11.8062 6.50732 12.2539 6.95504 12.2539 7.50732V7.51123C12.2539 8.06352 11.8062 8.51123 11.2539 8.51123H11.25C10.6977 8.51123 10.25 8.06352 10.25 7.51123V7.50732ZM16.75 11C16.6763 11 16.6029 11.0013 16.53 11.0037C13.5817 11.1038 11.5 13.1303 11.5 15.3112C11.5 15.5359 11.521 15.7559 11.5612 15.9704C11.9293 17.9298 14.0076 19.6224 16.75 19.6224C17.4593 19.6224 18.1322 19.5066 18.7446 19.2989C19.0172 19.2064 19.3165 19.2359 19.5658 19.3798L20.1671 19.727V19.0687C20.1671 18.7751 20.2962 18.4964 20.52 18.3064C21.4616 17.507 22 16.4444 22 15.3112C22 13.0766 19.8083 11 16.75 11ZM13.5561 13.656C13.5561 13.1037 14.0038 12.656 14.5561 12.656H14.56C15.1123 12.656 15.56 13.1037 15.56 13.656V13.6599C15.56 14.2122 15.1123 14.6599 14.56 14.6599H14.5561C14.0038 14.6599 13.5561 14.2122 13.5561 13.6599V13.656ZM17.9361 13.6599C17.9361 13.1076 18.3838 12.6599 18.9361 12.6599H18.94C19.4923 12.6599 19.94 13.1076 19.94 13.6599V13.6638C19.94 14.2161 19.4923 14.6638 18.94 14.6638H18.9361C18.3838 14.6638 17.9361 14.2161 17.9361 13.6638V13.6599Z" fill="rgb(95, 114, 146)" />
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 32 32"><defs><linearGradient id="vscodeIconsFileTypeWord0" x1="4.494" x2="13.832" y1="-1712.086" y2="-1695.914" gradientTransform="translate(0 1720)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#2368c4"/><stop offset=".5" stop-color="#1a5dbe"/><stop offset="1" stop-color="#1146ac"/></linearGradient></defs><path fill="#41a5ee" d="M28.806 3H9.705a1.19 1.19 0 0 0-1.193 1.191V9.5l11.069 3.25L30 9.5V4.191A1.19 1.19 0 0 0 28.806 3"/><path fill="#2b7cd3" d="M30 9.5H8.512V16l11.069 1.95L30 16Z"/><path fill="#185abd" d="M8.512 16v6.5l10.418 1.3L30 22.5V16Z"/><path fill="#103f91" d="M9.705 29h19.1A1.19 1.19 0 0 0 30 27.809V22.5H8.512v5.309A1.19 1.19 0 0 0 9.705 29"/><path d="M16.434 8.2H8.512v16.25h7.922a1.2 1.2 0 0 0 1.194-1.191V9.391A1.2 1.2 0 0 0 16.434 8.2" opacity="0.1"/><path d="M15.783 8.85H8.512V25.1h7.271a1.2 1.2 0 0 0 1.194-1.191V10.041a1.2 1.2 0 0 0-1.194-1.191" opacity="0.2"/><path d="M15.783 8.85H8.512V23.8h7.271a1.2 1.2 0 0 0 1.194-1.191V10.041a1.2 1.2 0 0 0-1.194-1.191" opacity="0.2"/><path d="M15.132 8.85h-6.62V23.8h6.62a1.2 1.2 0 0 0 1.194-1.191V10.041a1.2 1.2 0 0 0-1.194-1.191" opacity="0.2"/><path fill="url(#vscodeIconsFileTypeWord0)" d="M3.194 8.85h11.938a1.193 1.193 0 0 1 1.194 1.191v11.918a1.193 1.193 0 0 1-1.194 1.191H3.194A1.19 1.19 0 0 1 2 21.959V10.041A1.19 1.19 0 0 1 3.194 8.85"/><path fill="#fff" d="M6.9 17.988q.035.276.046.481h.028q.015-.195.065-.47c.05-.275.062-.338.089-.465l1.255-5.407h1.624l1.3 5.326a8 8 0 0 1 .162 1h.022a8 8 0 0 1 .135-.975l1.039-5.358h1.477l-1.824 7.748h-1.727l-1.237-5.126q-.054-.222-.122-.578t-.084-.52h-.021q-.021.189-.084.561t-.1.552L7.78 19.871H6.024L4.19 12.127h1.5l1.131 5.418a5 5 0 0 1 .079.443"/></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

2517
src/wxcomponents/agent-ui/index.js Executable file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
{
"component": true,
"usingComponents": {
"markdownPreview": "./wd-markdown/index",
"FoldedCard": "./collapse/index",
"chatFile": "./chatFile/index",
"feedback": "./feedback/index",
"customCard": "./customCard/index",
"tool": "./tool/index"
}
}

View File

@ -0,0 +1,391 @@
<!-- agent ui 组件根容器 -->
<view class="agent-ui" bind:tap="onTapPage">
<!-- 左侧抽屉,只在 bot 模式下展示 -->
<view wx-if="{{chatMode==='bot'&&!isAgent}}" class="drawer-mask {{isDrawerShow ? 'show' : ''}}" bindtap="closeDrawer"></view>
<view wx-if="{{chatMode==='bot'&&!isAgent}}" class="drawer {{isDrawerShow ? 'show' : ''}}">
<view class="drawer-header">
<view class="create-new-chat" bind:tap="clickCreateInDrawer">
<image style="width: 48rpx;height: 48rpx;margin-right: 10rpx" src="./imgs/chat-add.svg" mode="aspectFill" />
<text> 开启新对话 </text>
</view>
<image class="close-icon" src="./imgs/indent-left.svg" bindtap="closeDrawer" />
</view>
<view class="drawer-content">
<scroll-view enhanced="{{true}}" style="height: 100%;" show-scrollbar="{{false}}" scroll-y="true"
bindscrolltolower="scrollConToBottom">
<block wx:if="{{ conversations.length > 0 }}">
<view class="con-block" wx:if="{{transformConversations.todayCon.length}}">
<text class="date-title">今天</text>
<view class="con-container">
<view class="con-item {{item.conversationId === conversation.conversationId ? 'selected-con' : ''}}"
bind:tap="handleClickConversation" bind:longpress="handleLongPressConversation"
data-conversation="{{item}}" wx:for="{{transformConversations.todayCon}}" wx:key="index">
{{item.title}}
</view>
</view>
</view>
<view class="con-block" wx:if="{{transformConversations.curMonthCon.length}}">
<text class="date-title">本月</text>
<view class="con-container">
<view class="con-item {{item.conversationId === conversation.conversationId ? 'selected-con' : ''}}"
bind:tap="handleClickConversation" bind:longpress="handleLongPressConversation"
data-conversation="{{item}}" wx:for="{{transformConversations.curMonthCon}}" wx:key="index">
{{item.title}}
</view>
</view>
</view>
<view class="con-block" wx:if="{{transformConversations.earlyCon.length}}">
<text class="date-title">更早</text>
<view class="con-container">
<view class="con-item" bind:tap="handleClickConversation" bind:longpress="handleLongPressConversation"
data-conversation="{{item}}" wx:for="{{transformConversations.earlyCon}}" wx:key="index">
{{item.title}}
</view>
</view>
</view>
</block>
<block wx:else>
<view
style="width: 100%;height: 100%;display: flex;flex-direction: column;justify-content: center;align-items: center;">
<image src="./imgs/chat-bubble-history.svg" mode="aspectFill"
style="width: 100rpx;height: 100rpx;transform: translateY(-50%);" />
<text style="color: rgb(160, 160, 160)">暂无历史记录</text>
</view>
</block>
</scroll-view>
</view>
</view>
<!-- 标题栏包含会话列表展开按钮agent 名称,开启新会话按钮),只在 bot 模式下展示 -->
<view class="navBar {{showBotName ? 'showBotName' : 'hiddenBotName'}}" wx:if="{{chatMode === 'bot'&&!isAgent}}">
<view class="nav-content {{showBotName ? 'showBotName' : 'hiddenBotName'}}"
style="{{showMultiConversation ? 'justify-content: space-between;' : ''}}">
<image wx:if="{{bot.botId && showMultiConversation}}" bind:tap="openDrawer" class="con-icon"
src="./imgs/indent-right.svg" mode="aspectFill" />
<text wx:if="{{showBotName}}" class="bot-name">{{bot.name}}</text>
<image wx:if="{{bot.botId && showMultiConversation}}" class="con-icon" bind:tap="createNewConversation"
src="./imgs/chat-bubble-add.svg" mode="aspectFill" />
</view>
</view>
<view style="height: 100%;overflow: auto;position: relative;">
<!-- 聊天对话区 -->
<scroll-view bindwheel="onWheel" enhanced="{{true}}" bindscroll="onScroll" binddragstart="handleScrollStart"
class="main" style="height: 100%;" scroll-y="{{true}}" scroll-top="{{viewTop}}" scroll-into-view="{{ scrollTo }}"
lower-threshold="1" bindscrolltolower="handleScrollToLower" show-scrollbar="{{false}}"
refresher-enabled="{{showPullRefresh && (bot.multiConversationEnable ? conversation : true)}}"
refresher-threshold="{{80}}" bindrefresherrefresh="handleRefresh" refresher-triggered="{{triggered}}"
bounces="{{false}}">
<view class="contentBox" style="margin-bottom: 30px;">
<!-- 下拉刷新按钮,只在 bot 模式下展示 -->
<view
wx:if="{{chatMode === 'bot'&& !isAgent && bot.botId && showPullRefresh && (bot.multiConversationEnable ? conversation : true)}}"
class="tips">
{{refreshText}}
</view>
<!-- model 模式下的头像,只在 model 模式下展示 -->
<view wx:if="{{chatMode === 'model'}}" class="nav">
<image src="{{modelConfig.logo}}" mode="aspectFill" class="avatar" />
<view style="line-height: 47px; font-size: 20px; font-weight: 500;">{{modelConfig.modelProvider}}</view>
<view style="line-height: 26px;padding: 0px 16px; font-size: 32rpx;">{{modelConfig.welcomeMsg}}</view>
</view>
<block wx-if="{{!isAgent}}" wx:for="{{chatRecords}}" wx:key="record_id">
<!-- 系统聊天 -->
<view class="system" style="padding-left: {{showBotAvatar?80:0}}rpx;" wx:if="{{item.role==='assistant'}}">
<view class="avatar-left" wx:if="{{showBotAvatar}}">
<image src="{{chatMode==='bot'?bot.avatar:modelConfig.logo}}" mode="aspectFill"
style="width: 56rpx;height: 56rpx; border-radius: 28rpx;" />
</view>
<view>
<!-- 最后一条消息,并且是发送状态显示发送中 -->
<block wx:if="{{(chatRecords.length-1)===index&&chatStatus===1}}">
<view style="display: flex;align-items: center; gap: 4px; font-size: 32rpx;line-height: 1.8;">
<image src="./imgs/loading.svg" mode="aspectFill" style="width: 14px;height: 14px;" /> 请稍等,正在卖力思考中 🤔
</view>
</block>
<block wx:else>
<!-- 数据库检索 -->
<view wx:if="{{item.db_len}}"
style="border-radius: 8px;margin-bottom: 12px;background-color: #f5f5f5;padding: 18rpx 26rpx;display: inline-block;opacity: 0.7;font-size: 14px;">
已匹配 {{item.db_len}} 张数据表
</view>
<!-- 联网搜索 -->
<FoldedCard wx:if="{{item.search_info && item.search_info.search_results}}" initStatus="{{false}}"
showBgColor="{{true}}">
<view slot="title" style="opacity: 0.7;font-size: 14px;display: flex; align-items: center; gap: 8px;">
<image src="./imgs/search.svg" mode="aspectFill" style="width: 36rpx;height: 36rpx;" />
<text>已参考 {{item.search_info.search_results.length}} 个网页</text>
</view>
<view slot="content" class="link-box">
<block wx:for="{{item.search_info.search_results}}" wx:key="index">
<view bind:tap="copyUrl" data-url="{{item.url}}"
style="margin-bottom: 3px; font-size: 14px;color: rgb(0, 82, 217); line-height: 24px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;">
{{index+1}}.{{item.title}}
</view>
</block>
</view>
</FoldedCard>
<!-- 知识库 -->
<view wx:if="{{item.knowledge_base&&item.knowledge_base.length}}"
style="border-radius: 8px;margin-bottom: 12px;background-color: #f5f5f5;padding: 18rpx 26rpx;display: inline-block;opacity: 0.7;font-size: 14px;">
已参考 {{item.knowledge_base.length}} 处知识库内容
</view>
<!-- 推理过程 -->
<FoldedCard wx:if="{{!!item.reasoning_content}}" initStatus="{{true}}" showBgColor="{{false}}">
<view slot="title"
style="opacity: 0.7;font-size: 14px; display: flex; align-items: center; gap: 8px;">
<image src="./imgs/system-sum.svg" mode="aspectFill" style="width: 36rpx;height: 36rpx;" />
<block wx:if="{{item.pauseThinking}}">
已停止思考
</block>
<block wx:else>
<text>{{item.reasoning_content&&!item.content?"思考中...":"已深度思考(用时"+item.thinkingTime+"秒)"}}</text>
</block>
</view>
<view style="padding-left: 25rpx;margin-top: 28rpx; border-left: rgba(0,0,0,0.14) solid 2px ;"
slot="content">
<!-- <markdownPreview markdown="{{item.reasoning_content||''}}" fontSize="{{28}}"></markdownPreview> -->
<text user-select="{{true}}"
style="font-size: 14px;line-height: 1.75;color: #8b8b8b">{{item.reasoning_content||''}}</text>
</view>
</FoldedCard>
<!-- 工具调用 -->
<view wx:if="{{item.toolCallList && item.toolCallList.length > 0}}">
<block wx:for="{{item.toolCallList}}" wx:for-item="subItem" wx:key="id">
<markdownPreview markdown="{{subItem.content || ''}}"></markdownPreview>
<FoldedCard showExpandIcon="{{showToolCallDetail}}" initStatus="{{false}}" showBgColor="{{false}}">
<view slot="title"
style="opacity: 0.7;font-size: 14px; display: flex; align-items: center; gap: 8px;">
<block>
调用工具 {{subItem.name}}
<image wx:if="{{!subItem.callResult && !subItem.error}}" src="./imgs/loading.svg"
mode="aspectFill" style="width: 14px;height: 14px;" />
<image wx:if="{{subItem.callResult}}" mode="widthFix" src='./imgs/check.svg'
style="width: 36rpx; height: 36rpx;vertical-align: top;" bind:tap="share" />
<image wx:if="{{subItem.error}}" mode="widthFix" src='./imgs/close-red.svg'
style="width: 36rpx; height: 36rpx;vertical-align: top;" bind:tap="share" />
</block>
<!-- <block wx:else>
<text>{{item.reasoning_content&&!item.content?"思考中...":"已深度思考(用时"+item.thinkingTime+"秒)"}}</text>
</block> -->
</view>
<view wx:if="{{showToolCallDetail}}"
style="padding-left: 25rpx;margin-top: 28rpx; border-left: rgb(165, 164, 164) solid 2px; opacity: 0.7;"
slot="content">
<view>参数:</view>
<markdownPreview markdown="{{subItem.callParams||''}}" fontSize="{{28}}"></markdownPreview>
<view>结果:</view>
<markdownPreview markdown="{{subItem.callResult||''}}" fontSize="{{28}}"></markdownPreview>
</view>
</FoldedCard>
<customCard wx:if="{{subItem.rawResult}}" name="{{subItem.name}}" toolParams="{{subItem.rawParams}}"
toolData="{{subItem.rawResult}}"></customCard>
</block>
</view>
<!-- 正文 -->
<markdownPreview markdown="{{item.content||''}}"></markdownPreview>
<!-- 下面的按钮 -->
<view style="display: flex; gap: 10px;justify-content: flex;" wx:if="{{!item.hiddenBtnGround}}">
<image wx:if="{{item.error}}" mode="widthFix" bind:tap="showErrorMsg" src='./imgs/error-circle.svg'
class="tool_btn" data-content="{{item.error}}" data-reqid="{{item.reqId}}" />
<image mode="widthFix" bind:tap="copyChatRecord" src='./imgs/copy.svg' class="tool_btn"
data-content="{{item.content}}" />
<block wx:if="{{!item.error}}">
<button class="share_btn" open-type="share">
<image mode="widthFix" src='./imgs/share.svg' class="tool_btn" style="vertical-align: top;"
bind:tap="share" />
</button>
<block wx:if="{{chatMode=== 'bot'}}">
<image mode="widthFix" bind:tap="openFeedback" data-feedbackType="upvote"
data-feedbackRecordId="{{item.record_id}}" src='./imgs/thumb-up.svg' class="tool_btn" />
<image mode="widthFix" bind:tap="openFeedback" data-feedbackType="downvote"
data-feedbackRecordId="{{item.record_id}}" src='./imgs/thumb-down.svg' class="tool_btn" />
<block wx:if="{{item.record_id}}">
<image wx:if="{{audioContext.recordId !== item.record_id || audioContext.playStatus === 0}}"
mode="widthFix" bind:tap="handlePlayAudio" data-content="{{item.content}}"
data-recordId="{{item.record_id}}" src='./imgs/sound.svg' class="tool_btn" />
<image wx:elif="{{audioContext.playStatus === 1}}" mode="widthFix" src='./imgs/loading.svg'
class="tool_btn" />
<view wx:else class="playing_btn">
<image style="width: 36rpx;height: 36rpx;" mode="widthFix" bind:tap="handlePauseAudio"
data-recordId="{{item.record_id}}" src='./imgs/pause.svg' />
<image style="width: 30rpx;height: 30rpx" src="./imgs/playing.svg" mode="widthFix" />
<!-- 倍速切换按钮 -->
<view class="speed-switch" bindtap="toggleSpeedList" data-recordId="{{item.record_id}}">
<text class="speed-label">{{audioContext.currentSpeed || '1'}}</text>X
</view>
<!-- 倍速弹窗 -->
<view wx:if="{{audioContext.showSpeedList && audioContext.recordId === item.record_id}}"
class="speed-popup">
<view wx:for="{{speedList}}" wx:key="item" class="speed-option" bindtap="chooseSpeed"
data-speed="{{item}}" data-recordId="{{item.record_id}}">
<text>{{item}}X</text>
<image wx:if="{{audioContext.currentSpeed === item}}" src="./imgs/check.svg"
style="width: 24rpx;height: 24rpx;margin-left:8rpx;" />
</view>
</view>
</view>
</block>
</block>
</block>
</view>
<image wx:if="{{(chatRecords.length - 1) === index && (chatStatus === 2 || chatStatus === 3)}}"
mode="widthFix" src='./imgs/loading.svg' style="width: 14px;height: 14px;" />
</block>
</view>
</view>
<!-- 用户输入 -->
<view class="userContent" wx:if="{{item.role==='user'}}">
<view class="user" style="padding-left: {{showBotAvatar?80:0}}rpx;display: flex;">
<view class="user_content" bind:longpress="handleLongPress" data-content="{{item.content}}"
data-id="{{item.record_id}}">
{{item.content}}
<!-- 长按菜单 -->
<view class="operation-menu" wx:if="{{showMenu && tapMenuRecordId === item.record_id}}">
<view class="menu-item" bind:tap="handleCopyAll" data-content="{{item.content}}">
<image src="./imgs/copy.svg" class="menu-icon" />
<text>复制全文</text>
</view>
<view class="menu-item" bind:tap="handleEdit" data-content="{{item.content}}">
<image src="./imgs/edit.svg" class="menu-icon" />
<text>修改</text>
</view>
</view>
</view>
</view>
<view class="fileBar">
<chatFile enableDel="{{false}}" wx:for="{{item.fileList}}" wx:for-item="innerItem" wx:key="tempPath"
fileData="{{innerItem}}" bind:removeChild="handleRemoveChild" bind:changeChild="handleChangeChild">
</chatFile>
</view>
</view>
</block>
<!-- agentV2 聊天列表 -->
<block wx:if="{{isAgent}}" wx:for="{{messages}}" wx:key="id">
<view class="userContent" wx:if="{{item.role==='user'}}">
<view class="user">
<view class="user_content"> {{item.content}}</view>
</view>
</view>
<view class="system" wx:if="{{item.role==='assistant'}}">
<!-- 渲染 parts -->
<block wx:for="{{item.parts}}" wx:for-item="part" wx:key="id">
<block wx:if="{{part.type==='text'}}">
<markdownPreview markdown="{{part.content||''}}"></markdownPreview>
</block>
<block wx:if="{{part.type==='tool_call'}}">
<tool tool-name="{{part.toolCallName}}" tool-status="{{part.status}}" tool-params="{{part.arguments}}"
toolResult="{{part.result}}">
</tool>
</block>
<block wx:if="{{part.type==='error'}}">
<view>{{part.content||''}}</view>
</block>
</block>
</view>
</block>
<!-- 加载图标 -->
<view wx:if="{{isAgent&&chatStatus!=0}}">
<image src="./imgs/loading.svg" mode="aspectFill" style="width: 14px;height: 14px; margin-left: 32rpx;" />
</view>
<!-- 推荐问题,只在 bot 模式下展示 -->
<block wx-if="{{chatMode==='bot'&&!isAgent}}" wx:for="{{questions}}" wx:key="item">
<view class="questions" style="padding-left: {{showBotAvatar?80:0}}rpx;">
<view class="question_content" bind:tap="handleSendMessage" data-message="{{item}}">{{item}}</view>
</view>
</block>
</view>
<view id="scroll-bottom" style="width: 100%;height: 20px;"></view>
</scroll-view>
<!-- 页面不在底部时候的滚动到底按钮 -->
<image bind:tap="autoToBottom" wx:if="{{manualScroll&&!isAgent}}"
style="width:28px;height:28px;border-radius: 50px;position: absolute;bottom:150px;right: 20px;padding: 5px;background-color: white;box-shadow: rgba(99, 99, 99, 0.2) 0px 2px 8px 0px;"
src="./imgs/toBottom.svg" mode="aspectFit" binderror="" bindload="" />
</view>
<!-- 底部输入区 -->
<view class="footer">
<view class="{{ showFileList ? 'no_feature_list' : 'feature_list'}}" wx:if="{{showFeatureList}}">
<view bind:tap="handleClickWebSearch" class="{{'webSearchSwitch ' + (useWebSearch ? 'feature_enable' : '')}}">
<image src="{{ useWebSearch ? './imgs/internetUse.svg' : './imgs/internet.svg'}}" mode=""
style="width: 40rpx;height:30px;margin-right: 10rpx;" />
<text style="color: {{useWebSearch ? 'rgb(77, 107, 254)' : 'rgb(95, 114, 146)'}}">联网搜索</text>
</view>
</view>
<view class="file_list" wx:if="{{showFileList}}">
<chatFile enableDel="{{true}}" wx:for="{{sendFileList}}" wx:key="tempId" fileData="{{item}}"
bind:removeChild="handleRemoveChild" bind:changeChild="handleChangeChild"></chatFile>
</view>
<view class="foot_function">
<view class="input_box">
<view class="left_btns" wx:if="{{showVoice&&!isAgent}}">
<image wx:if="{{!useVoice}}" src="./imgs/voice.svg" class="set" mode="widthFix"
bind:tap="handleChangeInputType" />
<image wx:else src="./imgs/keyboard.svg" class="set" mode="widthFix" bind:tap="handleChangeInputType" />
</view>
<view hidden="{{useVoice}}" class="input_inner_box">
<textarea class="input" value="{{inputValue}}" maxlength="1024" bindfocus="bindInputFocus"
bindinput="bindKeyInput" placeholder="说点什么吧" bindconfirm="handleSendMessage" confirm-type="send"
adjust-position cursor-spacing="40" auto-height="{{true}}" show-confirm-bar="{{false}}"
bindlinechange="handleLineChange" />
</view>
<text
style="position: absolute;top: -50%;left: 50%;transform: translateX(-50%);font-size: 12px;color: {{sendStatus === 2 ? '#e84f50;' : ''}}">{{sendStatus === 1 ? "松开发送,上滑取消" : (sendStatus === 2 ? "松开取消" : "")}}</text>
<view hidden="{{!useVoice}}" bindtouchstart="handleTouchStart" bindtouchmove="handleTouchMove"
bindtouchend="handleTouchEnd" class="input_inner_box"
style="position: absolute;width: calc(100% - 136rpx);left: 50%;transform: translateX(-50%);opacity: 0;">
</view>
<view hidden="{{!useVoice}}" class="input_inner_box say_box"
style="background-color: {{sendStatus === 1 ? '#e9f6ef' : (sendStatus === 2 ? '#f8ecea' : '')}};"><text
wx:if="{{!longPressTriggered}}">{{voiceRecognizing ? '识别中' : '按住 说话'}}</text>
<image wx:else
src="{{ sendStatus === 1 ? './imgs/sendSaying.svg' : (sendStatus === 2 ? './imgs/cancelSaying.svg' : '')}}"
class="set" mode="widthFix" />
</view>
<view class="right_btns">
<!-- 加号 -->
<image src="./imgs/set.svg" class="set" mode="widthFix" bind:tap="handleClickTools" />
<!-- 发送按钮 -->
<view wx:if="{{!!inputValue&&chatStatus===0}}" class="set"
style="display: flex;justify-content: center;align-items: center;background-color: #436af4;border-radius: 50px;">
<image src="./imgs/send.svg" class="send-set" mode="widthFix" bind:tap="handleSendMessage"
style="transform-origin: 8px 8px;" />
</view>
<!-- 暂停按钮 -->
<image src="./imgs/stop.svg" class="set" mode="widthFix" wx:if="{{!(chatStatus===0)}}" bind:tap="stop" />
</view>
</view>
</view>
<!-- 底部工具栏 -->
<view class="tool_box" wx:if="{{showTools}}">
<view class="function" bind:tap="handleTapClear">
<image src="./imgs/clear.svg" alt="widthFix" class="icon" />
<text class="text_desc">清除</text>
</view>
<view wx:if="{{showUploadFile && chatMode === 'bot'&&!isAgent}}" class="function" bind:tap="handleUploadMessageFile">
<image src="./imgs/wechat.svg" alt="widthFix" class="icon" />
<text class="text_desc">微信文件</text>
</view>
<view wx:if="{{showUploadImg && chatMode === 'bot'&&!isAgent}}" class="function" bind:tap="handleAlbum">
<image src="./imgs/uploadImg.svg" alt="widthFix" class="icon" />
<text class="text_desc">图片</text>
</view>
<view wx:if="{{showUploadImg && chatMode === 'bot'&&!isAgent}}" class="function" bind:tap="handleCamera">
<image src="./imgs/camera.svg" alt="widthFix" class="icon" />
<text class="text_desc">相机</text>
</view>
</view>
</view>
<feedback input="{{input}}" aiAnswer="{{aiAnswer}}" isShowFeedback="{{isShowFeedback}}" bind:close="closefeedback"
feedbackRecordId="{{feedbackRecordId}}" feedbackType="{{feedbackType}}" botId="{{bot.botId}}"></feedback>
<!-- 底部操作菜单弹窗 -->
<view class="action-menu-modal" wx:if="{{showActionMenu}}" bind:tap="hideActionMenu">
<view class="action-menu" catchtap="">
<view class="action-item" bind:tap="handleDeleteConversation" data-conversation="{{selectedConversation}}">
删除
</view>
<view class="action-item cancel-item" bind:tap="hideActionMenu">
取消
</view>
</view>
</view>
</view>

View File

@ -0,0 +1,730 @@
/* components/agent-ui/index.wxss */
.agent-ui {
width: 750rpx;
height: 100vh;
position: relative;
display: flex;
flex-direction: column;
overflow:hidden;
color: #333;
background-color: #fff;
overflow: hidden;
touch-action: none; /* 增强禁止滚动效果 */
}
.showBotName {
height: 62px;
}
.hiddenBotName {
margin-top: 2px;
margin-bottom: 2px;
}
.nav {
width: 750rpx;
padding: 20px 0px 0px 0px;
display: flex;
flex-direction: column;
align-items: center;
flex-shrink: 0;
}
.navBar {
width: 100%;
box-shadow: 0 16px 16px #fff;
}
.tips {
display: flex;
align-items: center;
gap: 12px;
width: 100%;
color: rgb(128, 128, 128);
font-size: 12px;
height: 32px;
margin-bottom: 16px;
}
.tips::before,
.tips::after {
content: '';
display: inline-block;
height: 1px;
transform: scaleY(.5);
flex-grow: 1;
border-radius: 2px;
}
.tips::before {
background-image: linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.5));
}
.tips::after {
background-image: linear-gradient(to left, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.5));
}
.nav-content {
/* height:62px; */
display: flex;
align-items: center;
justify-content: space-around;
gap: 12px;
}
.bot-avatar {
width: 36px;
height: 36px;
border-radius: 50%;
border: 2px solid rgba(255, 255, 255, 0.8);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.bot-name {
font-size: 18px;
font-weight: 500;
color: #333;
}
.main {
background-color: #fff;
flex-grow: 1;
position: relative;
}
.share_btn {
background-color: #fff;
margin: 0px !important;
padding: 0rpx !important;
width: 64rpx !important;
height: 64rpx;
border: none !important;
}
.avatar {
width: 160rpx;
height: 160rpx;
border-radius: 75rpx;
}
.questions {
margin: 0px 16px 10px 16px;
}
.question_content {
background-color: #f5f5f5;
padding: 16rpx 24rpx;
border-radius: 12px;
display: inline-block;
font-size: 14px;
font-weight: 300;
}
.footer {
width: 100%;
min-height: 65px;
max-height: 380px;
flex-shrink: 0;
position: relative;
padding: 0px 16rpx 24px;
box-sizing: border-box;
}
.footer .file_list {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
gap: 16rpx;
overflow-x: scroll;
padding: 20rpx 0px;
}
.img-box {
position: absolute;
top: -100px;
left: 0px;
white-space: nowrap;
width: 100%;
background-color: #fff;
}
.img-preview {
display: inline-block;
width: 80px;
height: 80px;
margin-right: 8px;
position: relative;
margin-top: 10px;
}
.img-preview-image {
width: 80px;
height: 80px;
border-radius: 10px;
}
.img-preview-loading {
width: 100%;
height: 100%;
position: absolute;
top: 0px;
left: 0px;
background-color: #eee;
border-radius: 10px;
}
.img-preview-close {
width: 16px;
height: 16px;
position: absolute;
right: -8px;
top: -8px;
}
.input_box {
display: flex;
align-items: flex-end;
flex-direction: row;
gap: 10rpx;
position: relative;
min-height: 40px;
padding: 0px 0px;
background-color: white;
transition: all 0.3s;
}
.set_panel_modal {
position: fixed;
width: 750rpx;
height: 100vh;
left: 0px;
top: 0px;
background-color: rgba(0, 0, 0, 0.7);
z-index: 1000;
}
.set_panel {
background-color: #f3f3f3;
position: absolute;
left: 0px;
bottom: 0px;
width: 750rpx;
}
.set_panel_funtion {
display: flex;
flex-direction: row;
padding: 10px 16px;
box-sizing: border-box;
gap: 10px;
}
.set_panel_cancel {
height: 60px;
text-align: center;
line-height: 40px;
color: black;
border-top: #cfcdcd solid 1px;
}
.function {
display: flex;
width: 150rpx;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 8px;
background-color: #f3f4f6;
color: black;
padding: 12px 6px;
border-radius: 16px;
}
.icon {
width: 48rpx;
height: 48rpx;
}
.text_desc {
font-weight: 300;
font-size: 24rpx;
color: rgb(95, 114, 146);
}
.input_inner_box {
width: 100%;
min-height: 54px;
display: flex;
align-items: center;
border: #f3f3f3 solid 1px;
border-radius: 16px;
box-sizing: border-box;
background-color: #f3f4f6;
}
.say_box {
font-weight: bold;
justify-content: center;
}
.input {
padding: 8px;
color: black;
width: 100%;
flex: 1;
max-height: 160px;
font-size: 16px;
}
.say_btn {
width: 100%;
height: 100%;
}
.right_btns {
height: 50px;
display: flex;
align-items: center;
gap: 10rpx;
}
.left_btns {
height: 50px;
display: flex;
align-items: center;
gap: 10rpx;
}
.set {
width: 58rpx;
height: 58rpx;
}
.send-set {
width: 38rpx;
height: 38rpx;
}
.system {
margin-left: 24rpx;
margin-right: 24rpx;
border-radius: 12rpx;
margin-top: 12px;
padding-bottom: 16px;
box-sizing: border-box;
position: relative;
}
.avatar-left{
position:absolute;
top: 0px;
left: 0px;
}
.guide_system {
padding-left: 32rpx;
padding-right: 32rpx;
border-radius: 12rpx;
padding-bottom: 16px;
box-sizing: border-box;
}
.bot_intro_system {
padding-left: 32rpx;
padding-right: 32rpx;
border-radius: 12rpx;
padding-bottom: 16px;
box-sizing: border-box;
}
.user {
display: flex;
justify-content: flex-end;
}
.userContent {
margin-top: 12px;
padding-bottom: 16px;
}
.userContent .fileBar {
display: flex;
flex-direction: row;
flex-direction: row-reverse;
flex-wrap: nowrap;
padding: 0px 16px;
overflow-x: scroll;
max-height: 80px;
gap: 10px;
margin-top: 10px;
}
.user .user_content {
background-color: #f3f5fb;
border-radius: 12rpx 0rpx 12rpx 12rpx;
margin-left: 32rpx;
margin-right: 32rpx;
padding: 24rpx;
word-wrap: break-word;
word-break: break-all;
font-size: 32rpx;
position: relative;
}
.operation-menu {
position: absolute;
bottom: -120rpx;
left: 0;
background: rgba(0, 0, 0, 1);
border-radius: 8rpx;
display: flex;
padding: 16rpx 24rpx;
gap: 32rpx;
z-index: 100;
}
/* 添加三角形指示器 */
.operation-menu::before {
content: '';
position: absolute;
top: -16rpx; /* 调整三角形位置 */
left: 30rpx; /* 调整三角形的水平位置,使其指向文本 */
border-left: 16rpx solid transparent;
border-right: 16rpx solid transparent;
border-bottom: 16rpx solid rgba(0, 0, 0, 0.8);
width: 0;
height: 0;
}
.menu-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 8rpx;
}
.menu-item text {
color: #ffffff;
font-size: 24rpx;
}
.menu-icon {
width: 36rpx;
height: 36rpx;
filter: brightness(0) invert(1);
}
.feedback_modal {
position: fixed;
top: 0px;
left: 0px;
width: 750rpx;
height: 100vh;
background-color: rgba(0, 0, 0, 0.7);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.modal {
background-color: #fff;
width: 700rpx;
border-radius: 16rpx;
overflow: hidden;
}
.modal_head {
height: 40px;
line-height: 40px;
padding: 0px 10px;
}
.modal_body {
padding: 10px;
}
.modal_footer {
display: flex;
}
.link-box {
padding: 0px 16px 6px 16px;
}
.tool_box {
display: flex;
flex-direction: row;
box-sizing: border-box;
gap: 20rpx;
justify-content: flex-start;
flex-wrap: nowrap;
overflow-x: scroll;
padding: 20rpx 0rpx 0rpx;
}
.webSearchSwitch {
width: 200rpx;
height: 30px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 25px;
border: 1px solid rgba(0, 0, 0, 0.08);
font-size: 14px;
background-color: #fff;
}
.feature_enable {
background-color: rgb(219, 234, 254);
color: rgb(77, 107, 254);
border-color: rgba(0, 122, 255, 0.15);
}
.feature_list {
position: absolute;
background-color: transparent;
bottom: calc(100% + 20rpx);
}
.no_feature_list {
position: absolute;
background-color: transparent;
bottom: calc(100%);
}
/* 抽屉遮罩层 */
.drawer-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.4);
z-index: 998;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
}
.drawer-mask.show {
opacity: 1;
visibility: visible;
}
/* 抽屉主体 */
.drawer {
position: fixed;
top: 0;
left: -80%;
width: 80%;
height: 100vh;
background: #f9fbff;
z-index: 999;
transition: all 0.3s ease;
display: flex;
flex-direction: column;
}
.drawer.show {
left: 0;
}
.drawer-header {
padding: 16rpx 32rpx 16rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.close-icon {
width: 24px;
height: 24px;
}
.drawer-content {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden; /* 防止内容溢出 */
height: 0;
margin-bottom: 20px;
margin-left:32rpx;
margin-right:32rpx;
}
.con-icon {
width: 24px;
height: 24px;
margin-left: 16rpx;
margin-right: 16rpx;
}
.create-new-chat {
border-radius: 8px;
background: #dee9fc;
color: #4d6bfe;
cursor: pointer;
padding: 16rpx 24rpx;
display: flex;
justify-content: center;
align-items: center;
}
.date-title {
font-size: 16px;
color: rgb(128, 128, 128);
font-weight: 400;
}
.con-container {
margin-top: 12px;
}
.con-block {
margin-top: 16px;
}
.con-item {
padding: 12px 8px;
margin-bottom: 2px;
border-radius: 8px;
}
.con-item:active {
transition: filter 0.4s;
cursor: pointer;
background-color: rgb(249, 251, 255);
filter: brightness(0.95);
}
.selected-con {
background-color: rgb(249, 251, 255);
filter: brightness(95%);
transition: filter 0.4s;
}
.tool_btn {
width: 36rpx;
height: 36rpx;
padding: 10rpx;
border: 1rpx solid #cfcdcd;
border-radius: 14rpx;
}
.playing_btn {
height: 36rpx;
padding: 10rpx;
border: 1rpx solid #cfcdcd;
border-radius: 14rpx;
display: flex;
align-items: center;
gap: 10rpx;
position: relative;
}
.speed-switch {
display: flex;
align-items: center;
margin-left: 0rpx;
padding: 4rpx 12rpx;
border-radius: 20rpx;
/* background: #f5f5f7; */
font-size: 26rpx;
color: #222;
cursor: pointer;
}
.speed-label {
margin-left: 6rpx;
}
.speed-popup {
position: absolute;
bottom: 48rpx;
right: 0;
background: #fff;
border-radius: 12rpx;
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.08);
z-index: 99;
padding: 8rpx 0;
min-width: 80rpx;
}
.speed-option {
padding: 16rpx 32rpx;
font-size: 28rpx;
color: #222;
display: flex;
align-items: center;
justify-content: flex-start;
}
.speed-option:active {
background: #f0f0f0;
}
/* 底部操作菜单弹窗 */
.action-menu-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.4);
z-index: 1001;
display: flex;
flex-direction: column;
justify-content: flex-end;
animation: fadeIn 0.3s ease-out;
}
.action-menu {
background: #ffffff;
border-radius: 24rpx 24rpx 0 0;
overflow: hidden;
animation: slideUp 0.3s ease-out;
margin: 0 20rpx 20rpx 20rpx;
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.1);
}
.action-item {
padding: 32rpx 0;
text-align: center;
font-size: 32rpx;
color: #333333;
background: #ffffff;
border-bottom: 1rpx solid #f0f0f0;
position: relative;
}
.action-item:last-child {
border-bottom: none;
}
.action-item:active {
background: #f8f8f8;
}
.cancel-item {
margin-top: 20rpx;
border-radius: 24rpx;
border-bottom: none;
color: #666666;
}
/* 动画效果 */
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes slideUp {
from {
transform: translateY(100%);
}
to {
transform: translateY(0);
}
}

View File

@ -0,0 +1,2 @@
!function(n){"use strict";function d(n,t){var r=(65535&n)+(65535&t);return(n>>16)+(t>>16)+(r>>16)<<16|65535&r}function f(n,t,r,e,o,u){return d(function(n,t){return n<<t|n>>>32-t}(d(d(t,n),d(e,u)),o),r)}function l(n,t,r,e,o,u,c){return f(t&r|~t&e,n,t,o,u,c)}function g(n,t,r,e,o,u,c){return f(t&e|r&~e,n,t,o,u,c)}function v(n,t,r,e,o,u,c){return f(t^r^e,n,t,o,u,c)}function m(n,t,r,e,o,u,c){return f(r^(t|~e),n,t,o,u,c)}function i(n,t){var r,e,o,u,c;n[t>>5]|=128<<t%32,n[14+(t+64>>>9<<4)]=t;var f=1732584193,i=-271733879,a=-1732584194,h=271733878;for(r=0;r<n.length;r+=16)i=m(i=m(i=m(i=m(i=v(i=v(i=v(i=v(i=g(i=g(i=g(i=g(i=l(i=l(i=l(i=l(o=i,a=l(u=a,h=l(c=h,f=l(e=f,i,a,h,n[r],7,-680876936),i,a,n[r+1],12,-389564586),f,i,n[r+2],17,606105819),h,f,n[r+3],22,-1044525330),a=l(a,h=l(h,f=l(f,i,a,h,n[r+4],7,-176418897),i,a,n[r+5],12,1200080426),f,i,n[r+6],17,-1473231341),h,f,n[r+7],22,-45705983),a=l(a,h=l(h,f=l(f,i,a,h,n[r+8],7,1770035416),i,a,n[r+9],12,-1958414417),f,i,n[r+10],17,-42063),h,f,n[r+11],22,-1990404162),a=l(a,h=l(h,f=l(f,i,a,h,n[r+12],7,1804603682),i,a,n[r+13],12,-40341101),f,i,n[r+14],17,-1502002290),h,f,n[r+15],22,1236535329),a=g(a,h=g(h,f=g(f,i,a,h,n[r+1],5,-165796510),i,a,n[r+6],9,-1069501632),f,i,n[r+11],14,643717713),h,f,n[r],20,-373897302),a=g(a,h=g(h,f=g(f,i,a,h,n[r+5],5,-701558691),i,a,n[r+10],9,38016083),f,i,n[r+15],14,-660478335),h,f,n[r+4],20,-405537848),a=g(a,h=g(h,f=g(f,i,a,h,n[r+9],5,568446438),i,a,n[r+14],9,-1019803690),f,i,n[r+3],14,-187363961),h,f,n[r+8],20,1163531501),a=g(a,h=g(h,f=g(f,i,a,h,n[r+13],5,-1444681467),i,a,n[r+2],9,-51403784),f,i,n[r+7],14,1735328473),h,f,n[r+12],20,-1926607734),a=v(a,h=v(h,f=v(f,i,a,h,n[r+5],4,-378558),i,a,n[r+8],11,-2022574463),f,i,n[r+11],16,1839030562),h,f,n[r+14],23,-35309556),a=v(a,h=v(h,f=v(f,i,a,h,n[r+1],4,-1530992060),i,a,n[r+4],11,1272893353),f,i,n[r+7],16,-155497632),h,f,n[r+10],23,-1094730640),a=v(a,h=v(h,f=v(f,i,a,h,n[r+13],4,681279174),i,a,n[r],11,-358537222),f,i,n[r+3],16,-722521979),h,f,n[r+6],23,76029189),a=v(a,h=v(h,f=v(f,i,a,h,n[r+9],4,-640364487),i,a,n[r+12],11,-421815835),f,i,n[r+15],16,530742520),h,f,n[r+2],23,-995338651),a=m(a,h=m(h,f=m(f,i,a,h,n[r],6,-198630844),i,a,n[r+7],10,1126891415),f,i,n[r+14],15,-1416354905),h,f,n[r+5],21,-57434055),a=m(a,h=m(h,f=m(f,i,a,h,n[r+12],6,1700485571),i,a,n[r+3],10,-1894986606),f,i,n[r+10],15,-1051523),h,f,n[r+1],21,-2054922799),a=m(a,h=m(h,f=m(f,i,a,h,n[r+8],6,1873313359),i,a,n[r+15],10,-30611744),f,i,n[r+6],15,-1560198380),h,f,n[r+13],21,1309151649),a=m(a,h=m(h,f=m(f,i,a,h,n[r+4],6,-145523070),i,a,n[r+11],10,-1120210379),f,i,n[r+2],15,718787259),h,f,n[r+9],21,-343485551),f=d(f,e),i=d(i,o),a=d(a,u),h=d(h,c);return[f,i,a,h]}function a(n){var t,r="",e=32*n.length;for(t=0;t<e;t+=8)r+=String.fromCharCode(n[t>>5]>>>t%32&255);return r}function h(n){var t,r=[];for(r[(n.length>>2)-1]=void 0,t=0;t<r.length;t+=1)r[t]=0;var e=8*n.length;for(t=0;t<e;t+=8)r[t>>5]|=(255&n.charCodeAt(t/8))<<t%32;return r}function e(n){var t,r,e="0123456789abcdef",o="";for(r=0;r<n.length;r+=1)t=n.charCodeAt(r),o+=e.charAt(t>>>4&15)+e.charAt(15&t);return o}function r(n){return unescape(encodeURIComponent(n))}function o(n){return function(n){return a(i(h(n),8*n.length))}(r(n))}function u(n,t){return function(n,t){var r,e,o=h(n),u=[],c=[];for(u[15]=c[15]=void 0,16<o.length&&(o=i(o,8*n.length)),r=0;r<16;r+=1)u[r]=909522486^o[r],c[r]=1549556828^o[r];return e=i(u.concat(h(t)),512+8*t.length),a(i(c.concat(e),640))}(r(n),r(t))}function t(n,t,r){return t?r?u(t,n):function(n,t){return e(u(n,t))}(t,n):r?o(n):function(n){return e(o(n))}(n)}"function"==typeof define&&define.amd?define(function(){return t}):"object"==typeof module&&module.exports?module.exports=t:n.md5=t}(this);
//# sourceMappingURL=md5.min.js.map

View File

@ -0,0 +1,92 @@
Component({
/**
* 组件的属性列表
*/
properties: {
// 工具名称
toolName: {
type: String,
value: "",
},
// 工具状态success/failed/running
toolStatus: {
type: String,
value: "success",
},
// 工具参数
toolParams: {
type: String,
value: "",
},
toolResult: {
type: String,
value: "",
},
// 是否默认展开
defaultExpanded: {
type: Boolean,
value: false,
},
},
/**
* 组件的初始数据
*/
data: {
isExpanded: false,
parsedParams: "", // 解析后的参数对象
parsedResult: "", // 解析后的结果对象
},
observers: {
toolParams: function (toolParams) {
if (toolParams) {
try {
const parsed = JSON.stringify(JSON.parse(toolParams), null, 2);
console.log(parsed);
this.setData({ parsedParams: parsed });
} catch (e) {
// 解析失败返回原字符串
this.setData({ parsedParams: toolParams });
}
} else {
this.setData({ parsedParams: "" });
}
},
toolResult: function (toolResult) {
if (toolResult) {
try {
const parsed = JSON.stringify(JSON.parse(toolResult), null, 2);
console.log(parsed);
this.setData({ parsedResult: parsed });
} catch (e) {
// 解析失败返回原字符串
this.setData({ parsedResult: toolResult });
}
} else {
this.setData({ parsedResult: "" });
}
},
},
/**
* 组件的方法列表
*/
methods: {
// 切换展开/收起
toggleExpand() {
this.setData({
isExpanded: !this.data.isExpanded,
});
},
},
/**
* 生命周期函数
*/
attached() {
// 初始化展开状态
this.setData({
isExpanded: this.properties.defaultExpanded,
});
},
});

View File

@ -0,0 +1,5 @@
{
"component": true,
"usingComponents": {
}
}

View File

@ -0,0 +1,17 @@
<view class="tool">
<view class="header" bind:tap="toggleExpand">
<image wx:if="{{toolStatus==='running'}}" class="icon" src="../imgs/loading.svg" mode="aspectFill" />
<image wx:if="{{toolStatus==='success'}}" class="icon" src="../imgs/check.svg" mode="aspectFill" />
<image wx:if="{{toolStatus==='failed'}}" class="icon" src="../imgs/close-red.svg" mode="aspectFill" />
<view class="title">调用工具:{{toolName}}</view>
<image class="arrow" style="transform: rotate({{isExpanded?0:-90}}deg);" src="../imgs/arrow.svg"
mode="aspectFill" />
</view>
<view class="content" style='display: {{isExpanded?"block":"none"}}'>
<view class="content-title">参数:</view>
<view>{{parsedParams}}</view>
<view class="divider"></view>
<view class="content-title">调用结果:</view>
<view>{{parsedResult}}</view>
</view>
</view>

View File

@ -0,0 +1,45 @@
.tool {
background-color: #f5f5f5;
border-radius: 10rpx;
overflow: hidden;
margin-bottom: 10rpx;
}
.header {
display: flex;
align-items: center;
padding: 20rpx;
}
.title {
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.icon {
width: 48rpx;
height: 48rpx;
margin-right: 10px;
}
.arrow {
width: 48rpx;
height: 48rpx;
margin-left: 10rpx;
}
.content {
background-color: #333333;
padding: 20rpx;
color: #f5f5f5;
white-space: pre-wrap;
font-family: monospace;
}
.content-title{
margin-bottom: 10rpx;
}
.divider{
height: 20px;
}

View File

@ -0,0 +1,141 @@
export const checkConfig = (chatMode, agentConfig, modelConfig) => {
const { botId } = agentConfig || {};
const { modelProvider, quickResponseModel, deepReasoningModel } = modelConfig || {};
// 检测不在微信环境,提示用户
const appBaseInfo = wx.getAppBaseInfo();
try {
const systemInfo = wx.getSystemInfoSync();
if (systemInfo.environment === "wxwork") {
return [false, "请前往微信客户端扫码打开小程序"];
}
} catch (e) {
if (appBaseInfo.host.env === "SDK") {
return [false, "请前往微信客户端扫码打开小程序"];
}
}
// 检测AI能力不存在提示用户
if (compareVersions(appBaseInfo.SDKVersion, "3.7.7") < 0) {
return [false, "使用AI能力需基础库为3.7.7及以上,请升级基础库版本或微信客户端"];
}
if (!["bot", "model"].includes(chatMode)) {
return [false, "chatMode 不正确值应为“bot”或“model”"];
}
if (chatMode === "bot" && !botId) {
return [false, "当前chatMode值为bot请配置botId"];
}
if (chatMode === "model" && (!modelProvider || !quickResponseModel)) {
return [false, "当前chatMode值为model请配置modelProvider和quickResponseModel"];
}
return [true, ""];
};
// 随机选取三个问题
export function randomSelectInitquestion(question = [], num = 3) {
if (question.length <= num) {
return [...question];
}
const set = new Set();
while (set.size < num) {
const randomIndex = Math.floor(Math.random() * question.length);
set.add(question[randomIndex]);
}
return Array.from(set);
}
export const getCloudInstance = (function () {
let cloudInstance = null;
return async function (envShareConfig) {
if (cloudInstance) {
return cloudInstance;
}
// 如果开启了环境共享走环境共享的ai实例
if (envShareConfig && envShareConfig.resourceAppid && envShareConfig.resourceEnv) {
let instance = new wx.cloud.Cloud({
// 资源方 AppID
resourceAppid: envShareConfig.resourceAppid,
// 资源方环境 ID
resourceEnv: envShareConfig.resourceEnv,
});
await instance.init();
// 烦环境共享时创建实例没有把环境id挂在instance上这里手动挂上去如果你发现instance上有个env那么这个insatnce就是环境共享的云开发实例
instance.env = envShareConfig.resourceEnv;
cloudInstance = instance;
return cloudInstance;
} else {
cloudInstance = wx.cloud;
return cloudInstance;
}
};
})();
export const compareVersions = (version1, version2) => {
const v1Parts = version1.split(".").map(Number);
const v2Parts = version2.split(".").map(Number);
const maxLength = Math.max(v1Parts.length, v2Parts.length);
for (let i = 0; i < maxLength; i++) {
const num1 = v1Parts[i] || 0;
const num2 = v2Parts[i] || 0;
if (num1 > num2) {
return 1;
} else if (num1 < num2) {
return -1;
}
}
return 0;
};
let isDomainWarn = false;
export const commonRequest = async (options) => {
const cloudInstance = await getCloudInstance();
const self = this;
// 判断 当前sdk 版本是否 小于 3.8.1
const appBaseInfo = wx.getAppBaseInfo();
const { path } = options;
if (compareVersions(appBaseInfo.SDKVersion, "3.8.1") < 0) {
console.log("走wx request");
const cloudInstance = await getCloudInstance();
const { token } = await cloudInstance.extend.AI.bot.tokenManager.getToken();
const envId = cloudInstance.env || cloudInstance.extend.AI.bot.context.env;
return wx.request({
...options,
path: undefined,
url: `https://${envId}.api.tcloudbasegateway.com/v1/aibot/${path}`,
header: {
...options.header,
Authorization: `Bearer ${token}`,
},
fail: (e) => {
if (options.fail) {
options.fail.bind(self)(e);
if (e.errno === 600002 || e.errMsg.includes("url not in domain list")) {
let msg = `请前往微信公众平台 request 合法域名配置中添加云开发域名 https://${envId}.api.tcloudbasegateway.com`;
if (!isDomainWarn) {
isDomainWarn = true;
wx.showModal({
title: "提示",
content: msg,
complete: () => {
isDomainWarn = false;
},
});
}
}
}
},
});
} else {
const ai = cloudInstance.extend.AI;
return ai.request(options);
}
};
export const sleep = (timeout) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, timeout);
});
};

View File

@ -0,0 +1,26 @@
Component({
options: {
virtualHost: true,
},
data: {
},
properties: {
dataClipboardText: {
type: String,
value: '',
},
},
methods: {
// 复制到剪贴板
copyClipBoard: function () {
wx.setClipboardData({
data: this.data.dataClipboardText,
// success() {
// wx.getClipboardData({
// success() {},
// });
// },
});
},
},
});

View File

@ -0,0 +1,6 @@
{
"component": true,
"styleIsolation": "shared",
"usingComponents": {
}
}

View File

@ -0,0 +1,3 @@
<view class="markdown-it-code-copy" bindtap="copyClipBoard">
<image src="../../imgs/copy.svg" style="width: 32rpx !important;height: 32rpx !important" mode="aspectFill" />
</view>

View File

@ -0,0 +1,9 @@
.markdown-it-code-copy {
position: absolute;
top: 7.5px;
right: 6px;
cursor: pointer;
outline: none;
}

View File

@ -0,0 +1,105 @@
import MarkdownIt from './utils/markdown-it.min.js';
import highlight from './utils/highlight.min.js';
import hljsJs from './utils/hljs_javascript.min.js';
import hljsCss from './utils/hljs_css.min.js';
import { addCustomClassPlugin,copy } from './utils/plugin'
Component({
options: {
virtualHost: true,
},
properties: {
className: {
type: String,
value: '',
},
style: {
type: String,
value: '',
},
id: {
type: String,
value: '',
},
markdown: {
type: String,
value: '',
},
fontSize: {
type: Number,
value: 32
},
options: {
type: Object,
value: {},
},
},
data: {
__html: '',
mdInstance: null,
},
methods: {
init() {
const { options } = this.data;
const hljs = highlight();
const javascript = hljsJs();
const css = hljsCss();
hljs.registerLanguage('javascript', javascript);
hljs.registerLanguage('css', css);
const md = new MarkdownIt({
// 默认开启高亮
highlight: function (str, lang) {
if (lang && hljs.getLanguage(lang)) {
try {
return (
'<pre class="_pre"><code class="hljs">' +
hljs.highlight(str, { language: lang, ignoreIllegals: true }).value +
'</code></pre>'
);
} catch (__) { }
}
return '<pre class="_pre"><code class="hljs">' + str + '</code></pre>';
},
...options,
});
// console.log(md.renderer.rules)
md.use(copy);
addCustomClassPlugin(md)
this.setData({ mdInstance: md });
this.triggerEvent('onReady', { markdownInstance: md });
this.setData({
__html: md.render(this.data.markdown),
});
},
updateWidgetAPI() {
this.setReadonlyAttributes &&
this.setReadonlyAttributes({
value: this.properties.markdown,
markdownInstance: this.data.mdInstance,
updateMarkdownInstance: ({ markdownInstance }) => this.setData({ mdInstance: markdownInstance }),
});
},
},
observers: {
markdown: function () {
const { mdInstance } = this.data;
if (!mdInstance) return;
const html = mdInstance.render(this.data.markdown)
this.setData({
__html: html,
});
},
options: function () {
this.init();
},
'markdown,mdInstance': function () {
this.updateWidgetAPI();
},
},
lifetimes: {
attached() {
this.init();
this.updateWidgetAPI();
},
},
});

View File

@ -0,0 +1,7 @@
{
"component": true,
"styleIsolation": "shared",
"usingComponents": {
"mp-html":"./mp-html/index"
}
}

View File

@ -0,0 +1,3 @@
<view class="wd-markdown" style="font-size: {{fontSize}}rpx;">
<mp-html content="{{__html}}" scroll-table="{{true}}" selectable="{{true}}"/>
</view>

View File

@ -0,0 +1,235 @@
/*
markdown 样式
*/
.wd-markdown {
word-wrap: break-word;
word-break: normal;
text-align: justify;
}
.wd-markdown ._p {
margin-bottom: 10rpx;
line-height: 1.8;
word-break: break-all;
}
.wd-markdown ._ol {
margin-bottom: 30rpx;
padding-left: 1.2em;
}
.wd-markdown ._ul {
margin-bottom: 30rpx;
padding-left: 1.2em;
}
.wd-markdown ._hr {
border: none;
border-top: 3px solid #eee;
margin-bottom: 10rpx;
}
.wd-markdown ._pre {
max-width: 100%;
padding: 32rpx;
overflow-x: auto;
background-color: rgb(246, 248, 250);
border-radius: 12rpx;
font-family: monospace;
font-size: 1em;
line-height: 1.14285em;
border-radius: 12rpx;
margin-bottom: 10rpx;
}
.wd-markdown ._h1 {
border-bottom-style: double;
border-bottom-width: 6rpx;
font-size: 42rpx;
padding-bottom: 10rpx;
margin-bottom: 20rpx;
}
.wd-markdown ._h2 {
border-bottom-style: solid;
border-bottom-width: 1rpx;
font-size: 40rpx;
padding-bottom: 8rpx;
margin-bottom: 18rpx;
}
.wd-markdown ._h3 {
font-size: 38rpx;
padding-bottom: 6rpx;
margin-bottom: 12rpx;
}
.wd-markdown ._h4 {
font-size: 36rpx;
padding-bottom: 4rpx;
margin-bottom: 12rpx;
}
.wd-markdown ._h5 {
font-size: 34rpx;
padding-bottom: 2rpx;
margin-bottom: 12rpx;
}
.wd-markdown ._h6 {
margin-bottom: 12rpx;
}
.wd-markdown ._a {
margin: 0 8rpx;
border-bottom-width: 1rpx;
border-bottom-style: solid;
line-height: 1;
color: #0000ee;
}
.wd-markdown ._blockquote {
margin: 0 0 10px;
padding: 15px 20px;
background-color: #f1f2f3;
border-left: 5px solid #ccc;
color: #666;
font-style: italic;
}
.wd-markdown ._tableParent {
width: 100%;
overflow-x: auto;
}
.wd-markdown ._table {
min-width: fit-content;
border-collapse: collapse;
margin: 0;
overflow: hidden;
}
.wd-markdown ._table ._th,
.wd-markdown ._table ._td {
box-sizing: border-box;
position: relative;
min-width: 70px;
padding: 0px 5px;
border: 1px solid #e1e6f0;
vertical-align: top;
background-clip: padding-box;
}
.wd-markdown ._table ._th {
font-weight: bold;
background-color: #f5f7fa;
}
.wd-markdown ._table ._td {
background-color: white;
}
.wd-markdown ._table ._th > ._p,
.wd-markdown ._table ._td > ._p {
min-height: 1em;
}
.wd-markdown image {
width: 480rpx !important;
height: 480rpx !important;
}
/*!
Theme: GitHub
Description: Light theme as seen on github.com
Author: github.com
Maintainer: @Hirse
Updated: 2021-05-15
Outdated base version: https://github.com/primer/github-syntax-light
Current colors taken from GitHub's CSS
*/
.hljs {
color: #24292e;
/* background: #ffffff; */
}
.hljs-doctag,
.hljs-keyword,
.hljs-meta .hljs-keyword,
.hljs-template-tag,
.hljs-template-variable,
.hljs-type,
.hljs-variable.language_ {
/* prettylights-syntax-keyword */
color: #d73a49;
}
.hljs-title,
.hljs-title.class_,
.hljs-title.class_.inherited__,
.hljs-title.function_ {
/* prettylights-syntax-entity */
color: #6f42c1;
}
.hljs-attr,
.hljs-attribute,
.hljs-literal,
.hljs-meta,
.hljs-number,
.hljs-operator,
.hljs-variable,
.hljs-selector-attr,
.hljs-selector-class,
.hljs-selector-id {
/* prettylights-syntax-constant */
color: #005cc5;
}
.hljs-regexp,
.hljs-string,
.hljs-meta .hljs-string {
/* prettylights-syntax-string */
color: #032f62;
}
.hljs-built_in,
.hljs-symbol {
/* prettylights-syntax-variable */
color: #e36209;
}
.hljs-comment,
.hljs-code,
.hljs-formula {
/* prettylights-syntax-comment */
color: #6a737d;
}
.hljs-name,
.hljs-quote,
.hljs-selector-tag,
.hljs-selector-pseudo {
/* prettylights-syntax-entity-tag */
color: #22863a;
}
.hljs-subst {
/* prettylights-syntax-storage-modifier-import */
color: #24292e;
}
.hljs-section {
/* prettylights-syntax-markup-heading */
color: #005cc5;
font-weight: bold;
}
.hljs-bullet {
/* prettylights-syntax-markup-list */
color: #735c0f;
}
.hljs-emphasis {
/* prettylights-syntax-markup-italic */
color: #24292e;
font-style: italic;
}
.hljs-strong {
/* prettylights-syntax-markup-bold */
color: #24292e;
font-weight: bold;
}
.hljs-addition {
/* prettylights-syntax-markup-inserted */
color: #22863a;
background-color: #f0fff4;
}
.hljs-deletion {
/* prettylights-syntax-markup-deleted */
color: #b31d28;
background-color: #ffeef0;
}
.hljs-char.escape_,
.hljs-link,
.hljs-params,
.hljs-property,
.hljs-punctuation,
.hljs-tag {
/* purposely ignored */
}

View File

@ -0,0 +1,8 @@
"use strict";function e(t){"@babel/helpers - typeof";return(e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(t)}function t(e,t,o){return(t=n(t))in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function n(t){var n=o(t,"string");return"symbol"==e(n)?n:n+""}function o(t,n){if("object"!=e(t)||!t)return t;var o=t[Symbol.toPrimitive];if(void 0!==o){var i=o.call(t,n||"default");if("object"!=e(i))return i;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===n?String:Number)(t)}/*!
* mp-html v2.5.0
* https://github.com/jin-yufeng/mp-html
*
* Released under the MIT license
* Author: Jin Yufeng
*/
var i=require("./parser"),r=[];Component({data:{nodes:[]},properties:{containerStyle:String,content:{type:String,value:"",observer:function(e){this.setContent(e)}},copyLink:{type:Boolean,value:!0},domain:String,errorImg:String,lazyLoad:Boolean,loadingImg:String,pauseVideo:{type:Boolean,value:!0},previewImg:{type:null,value:!0},scrollTable:Boolean,selectable:null,setTitle:{type:Boolean,value:!0},showImgMenu:{type:Boolean,value:!0},tagStyle:Object,useAnchor:null},created:function(){this.plugins=[];for(var e=r.length;e--;)this.plugins.push(new r[e](this))},detached:function(){this._hook("onDetached")},methods:{in:function(e,t,n){e&&t&&n&&(this._in={page:e,selector:t,scrollTop:n})},navigateTo:function(e,n){var o=this;return new Promise(function(i,r){if(!o.data.useAnchor)return void r(Error("Anchor is disabled"));var a=wx.createSelectorQuery().in(o._in?o._in.page:o).select((o._in?o._in.selector:"._root")+(e?"".concat(">>>","#").concat(e):"")).boundingClientRect();o._in?a.select(o._in.selector).scrollOffset().select(o._in.selector).boundingClientRect():a.selectViewport().scrollOffset(),a.exec(function(e){if(!e[0])return void r(Error("Label not found"));var a=e[1].scrollTop+e[0].top-(e[2]?e[2].top:0)+(n||parseInt(o.data.useAnchor)||0);o._in?o._in.page.setData(t({},o._in.scrollTop,a)):wx.pageScrollTo({scrollTop:a,duration:300}),i()})})},getText:function(e){var t="";return function e(n){for(var o=0;o<n.length;o++){var i=n[o];if("text"===i.type)t+=i.text.replace(/&amp;/g,"&");else if("br"===i.name)t+="\n";else{var r="p"===i.name||"div"===i.name||"tr"===i.name||"li"===i.name||"h"===i.name[0]&&i.name[1]>"0"&&i.name[1]<"7";r&&t&&"\n"!==t[t.length-1]&&(t+="\n"),i.children&&e(i.children),r&&"\n"!==t[t.length-1]?t+="\n":"td"!==i.name&&"th"!==i.name||(t+="\t")}}}(e||this.data.nodes),t},getRect:function(){var e=this;return new Promise(function(t,n){wx.createSelectorQuery().in(e).select("._root").boundingClientRect().exec(function(e){return e[0]?t(e[0]):n(Error("Root label not found"))})})},pauseMedia:function(){for(var e=(this._videos||[]).length;e--;)this._videos[e].pause()},setPlaybackRate:function(e){this.playbackRate=e;for(var t=(this._videos||[]).length;t--;)this._videos[t].playbackRate(e)},setContent:function(e,t){var n=this;this.imgList&&t||(this.imgList=[]),this._videos=[];var o={},r=new i(this).parse(e);if(t)for(var a=this.data.nodes.length,s=r.length;s--;)o["nodes[".concat(a+s,"]")]=r[s];else o.nodes=r;if(this.setData(o,function(){n._hook("onLoad"),n.triggerEvent("load")}),this.data.lazyLoad||this.imgList._unloadimgs<this.imgList.length/2){var l=0,c=function(e){e&&e.height||(e={}),e.height===l?n.triggerEvent("ready",e):(l=e.height,setTimeout(function(){n.getRect().then(c).catch(c)},350))};this.getRect().then(c).catch(c)}else this.imgList._unloadimgs||this.getRect().then(function(e){n.triggerEvent("ready",e)}).catch(function(){n.triggerEvent("ready",{})})},_hook:function(e){for(var t=r.length;t--;)this.plugins[t][e]&&this.plugins[t][e]()},_add:function(e){e.detail.root=this}}});

View File

@ -0,0 +1 @@
{"component":true,"usingComponents":{"node":"./node/node"}}

Some files were not shown because too many files have changed in this diff Show More