style: 优化移动端页面样式,调整组件布局,增强用户体验

This commit is contained in:
Junsen
2025-08-08 17:35:35 +08:00
parent 90f7af7afa
commit 994715d9eb
2 changed files with 98 additions and 557 deletions
@@ -432,7 +432,7 @@ function loadPluginFunctions() {
getPluginFunctions().then((res) => { getPluginFunctions().then((res) => {
const processedFunctions = res?.map((item) => { const processedFunctions = res?.map((item) => {
const meta = JSON.parse(item.fields || '[]') const meta = JSON.parse(item.fields || '[]')
const params = meta.reduce((m, f) => { const params = meta.reduce((m: any, f: any) => {
m[f.key] = f.default m[f.key] = f.default
return m return m
}, {}) }, {})
@@ -497,7 +497,7 @@ onMounted(async () => {
</script> </script>
<template> <template>
<view class="page-container"> <view class="h-screen flex flex-col bg-[#f5f7fb]">
<!-- 导航栏 --> <!-- 导航栏 -->
<wd-navbar title="助手设置" safe-area-inset-top> <wd-navbar title="助手设置" safe-area-inset-top>
<template #left> <template #left>
@@ -512,40 +512,40 @@ onMounted(async () => {
<scroll-view <scroll-view
scroll-y scroll-y
:style="{ height: `calc(100vh - ${safeAreaInsets?.top || 0}px - 120rpx)` }" :style="{ height: `calc(100vh - ${safeAreaInsets?.top || 0}px - 120rpx)` }"
class="main-content" class="flex-1 px-[20rpx] bg-[#f5f7fb] box-border"
enable-back-to-top enable-back-to-top
> >
<!-- 基础信息标题 --> <!-- 基础信息标题 -->
<view class="section-title"> <view class="pb-[20rpx] first:pt-[20rpx]">
<text class="title-text"> <text class="text-[36rpx] font-bold text-[#232338]">
基础信息 基础信息
</text> </text>
</view> </view>
<!-- 基础信息卡片 --> <!-- 基础信息卡片 -->
<view class="settings-card plain-card"> <view class="bg-[#fbfbfb] rounded-[20rpx] mb-[24rpx] p-[24rpx] border border-[#eeeeee]" style="box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);">
<view class="form-field"> <view class="mb-[24rpx] last:mb-0">
<text class="field-label"> <text class="block text-[28rpx] text-[#232338] font-medium mb-[12rpx]">
助手昵称 助手昵称
</text> </text>
<input <input
v-model="formData.agentName" v-model="formData.agentName"
class="field-input" class="w-full h-[80rpx] p-[16rpx_20rpx] bg-[#f5f7fb] rounded-[12rpx] border border-[#eeeeee] text-[28rpx] text-[#232338] box-border leading-[1.4] outline-none focus:border-[#336cff] focus:bg-white placeholder:text-[#9d9ea3]"
type="text" type="text"
placeholder="请输入助手昵称" placeholder="请输入助手昵称"
> >
</view> </view>
<view class="form-field"> <view class="mb-[24rpx] last:mb-0">
<text class="field-label"> <text class="block text-[28rpx] text-[#232338] font-medium mb-[12rpx]">
角色模式 角色模式
</text> </text>
<view class="role-tags"> <view class="flex flex-wrap gap-[12rpx] mt-0">
<view <view
v-for="template in roleTemplates" v-for="template in roleTemplates"
:key="template.id" :key="template.id"
class="role-tag" class="px-[24rpx] py-[12rpx] bg-[rgba(51,108,255,0.1)] text-[#336cff] rounded-[20rpx] text-[24rpx] border border-[rgba(51,108,255,0.2)] cursor-pointer transition-all duration-300"
:class="{ active: selectedTemplateId === template.id }" :class="{ 'bg-[#336cff] text-white border-[#336cff]': selectedTemplateId === template.id }"
@click="selectRoleTemplate(template.id)" @click="selectRoleTemplate(template.id)"
> >
{{ template.agentName }} {{ template.agentName }}
@@ -553,129 +553,129 @@ onMounted(async () => {
</view> </view>
</view> </view>
<view class="form-field"> <view class="mb-[24rpx] last:mb-0">
<text class="field-label"> <text class="block text-[28rpx] text-[#232338] font-medium mb-[12rpx]">
角色介绍 角色介绍
</text> </text>
<textarea <textarea
v-model="formData.systemPrompt" v-model="formData.systemPrompt"
:maxlength="2000" :maxlength="2000"
placeholder="请输入角色介绍" placeholder="请输入角色介绍"
class="field-textarea" class="w-full h-[500rpx] p-[20rpx] bg-[#f5f7fb] rounded-[12rpx] border border-[#eeeeee] text-[26rpx] text-[#232338] leading-[1.6] resize-none box-border outline-none break-words break-all focus:border-[#336cff] focus:bg-white placeholder:text-[#9d9ea3]"
/> />
<view class="char-count"> <view class="text-right text-[22rpx] text-[#9d9ea3] mt-[8rpx]">
{{ (formData.systemPrompt || '').length }}/2000 {{ (formData.systemPrompt || '').length }}/2000
</view> </view>
</view> </view>
</view> </view>
<!-- 模型配置标题 --> <!-- 模型配置标题 -->
<view class="section-title"> <view class="pb-[20rpx]">
<text class="title-text"> <text class="text-[36rpx] font-bold text-[#232338]">
模型配置 模型配置
</text> </text>
</view> </view>
<!-- 模型配置卡片 --> <!-- 模型配置卡片 -->
<view class="settings-card plain-card"> <view class="bg-[#fbfbfb] rounded-[20rpx] mb-[24rpx] p-[24rpx] border border-[#eeeeee]" style="box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);">
<view class="model-grid"> <view class="flex flex-col gap-[16rpx]">
<view class="model-item" @click="openPicker('vad')"> <view class="flex items-center justify-between p-[20rpx] bg-[#f5f7fb] rounded-[12rpx] border border-[#eeeeee] cursor-pointer transition-all duration-300 active:bg-[#eef3ff]" @click="openPicker('vad')">
<text class="model-label"> <text class="text-[28rpx] text-[#232338] font-medium">
语音活动检测 语音活动检测
</text> </text>
<text class="model-value"> <text class="flex-1 text-right text-[26rpx] text-[#65686f] mx-[16rpx]">
{{ displayNames.vad }} {{ displayNames.vad }}
</text> </text>
<wd-icon name="arrow-right" custom-class="arrow-icon" /> <wd-icon name="arrow-right" custom-class="text-[20rpx] text-[#9d9ea3]" />
</view> </view>
<view class="model-item" @click="openPicker('asr')"> <view class="flex items-center justify-between p-[20rpx] bg-[#f5f7fb] rounded-[12rpx] border border-[#eeeeee] cursor-pointer transition-all duration-300 active:bg-[#eef3ff]" @click="openPicker('asr')">
<text class="model-label"> <text class="text-[28rpx] text-[#232338] font-medium">
语音识别 语音识别
</text> </text>
<text class="model-value"> <text class="flex-1 text-right text-[26rpx] text-[#65686f] mx-[16rpx]">
{{ displayNames.asr }} {{ displayNames.asr }}
</text> </text>
<wd-icon name="arrow-right" custom-class="arrow-icon" /> <wd-icon name="arrow-right" custom-class="text-[20rpx] text-[#9d9ea3]" />
</view> </view>
<view class="model-item" @click="openPicker('llm')"> <view class="flex items-center justify-between p-[20rpx] bg-[#f5f7fb] rounded-[12rpx] border border-[#eeeeee] cursor-pointer transition-all duration-300 active:bg-[#eef3ff]" @click="openPicker('llm')">
<text class="model-label"> <text class="text-[28rpx] text-[#232338] font-medium">
大语言模型 大语言模型
</text> </text>
<text class="model-value"> <text class="flex-1 text-right text-[26rpx] text-[#65686f] mx-[16rpx]">
{{ displayNames.llm }} {{ displayNames.llm }}
</text> </text>
<wd-icon name="arrow-right" custom-class="arrow-icon" /> <wd-icon name="arrow-right" custom-class="text-[20rpx] text-[#9d9ea3]" />
</view> </view>
<view class="model-item" @click="openPicker('vllm')"> <view class="flex items-center justify-between p-[20rpx] bg-[#f5f7fb] rounded-[12rpx] border border-[#eeeeee] cursor-pointer transition-all duration-300 active:bg-[#eef3ff]" @click="openPicker('vllm')">
<text class="model-label"> <text class="text-[28rpx] text-[#232338] font-medium">
视觉大模型 视觉大模型
</text> </text>
<text class="model-value"> <text class="flex-1 text-right text-[26rpx] text-[#65686f] mx-[16rpx]">
{{ displayNames.vllm }} {{ displayNames.vllm }}
</text> </text>
<wd-icon name="arrow-right" custom-class="arrow-icon" /> <wd-icon name="arrow-right" custom-class="text-[20rpx] text-[#9d9ea3]" />
</view> </view>
<view class="model-item" @click="openPicker('intent')"> <view class="flex items-center justify-between p-[20rpx] bg-[#f5f7fb] rounded-[12rpx] border border-[#eeeeee] cursor-pointer transition-all duration-300 active:bg-[#eef3ff]" @click="openPicker('intent')">
<text class="model-label"> <text class="text-[28rpx] text-[#232338] font-medium">
意图识别 意图识别
</text> </text>
<text class="model-value"> <text class="flex-1 text-right text-[26rpx] text-[#65686f] mx-[16rpx]">
{{ displayNames.intent }} {{ displayNames.intent }}
</text> </text>
<wd-icon name="arrow-right" custom-class="arrow-icon" /> <wd-icon name="arrow-right" custom-class="text-[20rpx] text-[#9d9ea3]" />
</view> </view>
<view class="model-item collapsible" @click="openPicker('memory')"> <view class="flex items-center justify-between p-[20rpx] bg-[#f5f7fb] rounded-[12rpx] border border-[#eeeeee] cursor-pointer transition-all duration-300 active:bg-[#eef3ff]" @click="openPicker('memory')">
<text class="model-label"> <text class="text-[28rpx] text-[#232338] font-medium">
记忆 记忆
</text> </text>
<text class="model-value"> <text class="flex-1 text-right text-[26rpx] text-[#65686f] mx-[16rpx]">
{{ displayNames.memory }} {{ displayNames.memory }}
</text> </text>
<wd-icon name="arrow-right" custom-class="arrow-icon" /> <wd-icon name="arrow-right" custom-class="text-[20rpx] text-[#9d9ea3]" />
</view> </view>
</view> </view>
</view> </view>
<!-- 语音设置标题 --> <!-- 语音设置标题 -->
<view class="section-title"> <view class="pb-[20rpx]">
<text class="title-text"> <text class="text-[36rpx] font-bold text-[#232338]">
语音设置 语音设置
</text> </text>
</view> </view>
<!-- 语音设置卡片 --> <!-- 语音设置卡片 -->
<view class="settings-card plain-card"> <view class="bg-[#fbfbfb] rounded-[20rpx] mb-[24rpx] p-[24rpx] border border-[#eeeeee]" style="box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);">
<view class="voice-settings"> <view class="flex flex-col gap-[16rpx]">
<view class="voice-item" @click="openPicker('tts')"> <view class="flex items-center justify-between p-[20rpx] bg-[#f5f7fb] rounded-[12rpx] border border-[#eeeeee] cursor-pointer transition-all duration-300 active:bg-[#eef3ff]" @click="openPicker('tts')">
<text class="voice-label"> <text class="text-[28rpx] text-[#232338] font-medium">
语音合成 语音合成
</text> </text>
<text class="voice-value"> <text class="flex-1 text-right text-[26rpx] text-[#65686f] mx-[16rpx]">
{{ displayNames.tts }} {{ displayNames.tts }}
</text> </text>
<wd-icon name="arrow-right" custom-class="arrow-icon" /> <wd-icon name="arrow-right" custom-class="text-[20rpx] text-[#9d9ea3]" />
</view> </view>
<view class="voice-item" @click="openPicker('voiceprint')"> <view class="flex items-center justify-between p-[20rpx] bg-[#f5f7fb] rounded-[12rpx] border border-[#eeeeee] cursor-pointer transition-all duration-300 active:bg-[#eef3ff]" @click="openPicker('voiceprint')">
<text class="voice-label"> <text class="text-[28rpx] text-[#232338] font-medium">
角色音色 角色音色
</text> </text>
<text class="voice-value"> <text class="flex-1 text-right text-[26rpx] text-[#65686f] mx-[16rpx]">
{{ displayNames.voiceprint }} {{ displayNames.voiceprint }}
</text> </text>
<wd-icon name="arrow-right" custom-class="arrow-icon" /> <wd-icon name="arrow-right" custom-class="text-[20rpx] text-[#9d9ea3]" />
</view> </view>
<view class="edit-functions"> <view class="flex items-center justify-between p-[20rpx] bg-[#f5f7fb] rounded-[12rpx] border border-[#eeeeee]">
<view class="text-[28rpx] text-[#232338] fw-[500]"> <view class="text-[28rpx] text-[#232338] font-medium">
插件 插件
</view> </view>
<view class="function-btn" @click="handleTools"> <view class="px-[24rpx] py-[12rpx] bg-[rgba(51,108,255,0.1)] text-[#336cff] rounded-[20rpx] text-[24rpx] cursor-pointer transition-all duration-300 active:bg-[#336cff] active:text-white" @click="handleTools">
<text>编辑功能</text> <text>编辑功能</text>
</view> </view>
</view> </view>
@@ -683,31 +683,31 @@ onMounted(async () => {
</view> </view>
<!-- 记忆历史标题 --> <!-- 记忆历史标题 -->
<view class="section-title"> <view class="pb-[20rpx]">
<text class="title-text"> <text class="text-[36rpx] font-bold text-[#232338]">
历史记忆 历史记忆
</text> </text>
</view> </view>
<!-- 记忆历史卡片 --> <!-- 记忆历史卡片 -->
<view class="settings-card plain-card"> <view class="bg-[#fbfbfb] rounded-[20rpx] mb-[24rpx] p-[24rpx] border border-[#eeeeee]" style="box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);">
<view class="form-field"> <view class="mb-[24rpx] last:mb-0">
<textarea <textarea
v-model="formData.summaryMemory" v-model="formData.summaryMemory"
placeholder="记忆内容" placeholder="记忆内容"
disabled disabled
class="field-textarea disabled-textarea" class="w-full h-[500rpx] p-[20rpx] bg-[#f0f0f0] text-[#65686f] opacity-80 rounded-[12rpx] border border-[#eeeeee] text-[26rpx] leading-[1.6] resize-none box-border outline-none break-words break-all"
/> />
</view> </view>
</view> </view>
<!-- 保存按钮 --> <!-- 保存按钮 -->
<view class="save-section"> <view class="p-0 mt-[40rpx]">
<wd-button <wd-button
type="primary" type="primary"
:loading="saving" :loading="saving"
:disabled="saving" :disabled="saving"
custom-class="save-btn" custom-class="w-full h-[80rpx] rounded-[16rpx] text-[30rpx] font-semibold bg-[#336cff] active:bg-[#2d5bd1]"
@click="saveAgent" @click="saveAgent"
> >
{{ saving ? '保存中...' : '保存' }} {{ saving ? '保存中...' : '保存' }}
@@ -780,306 +780,4 @@ onMounted(async () => {
@select="handleAgentSwitch" @select="handleAgentSwitch"
/> />
</view> </view>
</template> </template>
<style lang="scss" scoped>
.page-container {
display: flex;
flex-direction: column;
height: 100vh;
background: #f5f7fb;
}
.status-bar {
background: #ffffff;
width: 100%;
}
.main-content {
flex: 1;
padding: 0 20rpx;
background: #f5f7fb;
box-sizing: border-box;
}
.section-title {
padding: 0 0 20rpx 0;
&:first-child {
padding-top: 20rpx;
}
.title-text {
font-size: 36rpx;
font-weight: 700;
color: #232338;
}
}
.settings-card.plain-card {
padding: 24rpx;
}
.settings-card {
background: #fbfbfb;
border-radius: 20rpx;
margin-bottom: 24rpx;
padding: 32rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
border: 1rpx solid #eeeeee;
}
.form-field {
margin-bottom: 24rpx;
&:last-child {
margin-bottom: 0;
}
}
.field-label {
display: block;
font-size: 28rpx;
color: #232338;
font-weight: 500;
margin-bottom: 12rpx;
&.disabled-label {
color: #65686f;
}
}
.field-input {
width: 100%;
padding: 16rpx 20rpx;
height: 80rpx;
background: #f5f7fb;
border-radius: 12rpx;
border: 1rpx solid #eeeeee;
font-size: 28rpx;
color: #232338;
box-sizing: border-box;
line-height: 1.4;
outline: none;
&:focus {
border-color: #336cff;
background: #ffffff;
}
&::placeholder {
color: #9d9ea3;
}
}
.field-textarea {
width: 100%;
height: 500rpx;
padding: 20rpx;
background: #f5f7fb;
border-radius: 12rpx;
border: 1rpx solid #eeeeee;
font-size: 26rpx;
color: #232338;
line-height: 1.6;
resize: none;
box-sizing: border-box;
outline: none;
word-wrap: break-word;
word-break: break-all;
&:focus {
border-color: #336cff;
background: #ffffff;
}
&.disabled-textarea {
background: #f0f0f0;
color: #65686f;
opacity: 0.8;
}
&::placeholder {
color: #9d9ea3;
}
}
.char-count {
text-align: right;
font-size: 22rpx;
color: #9d9ea3;
margin-top: 8rpx;
}
.role-tags {
display: flex;
flex-wrap: wrap;
gap: 12rpx;
margin-top: 0;
}
.role-tag {
padding: 12rpx 24rpx;
background: rgba(51, 108, 255, 0.1);
color: #336cff;
border-radius: 20rpx;
font-size: 24rpx;
border: 1rpx solid rgba(51, 108, 255, 0.2);
cursor: pointer;
transition: all 0.3s ease;
&.active {
background: #336cff;
color: white;
border-color: #336cff;
}
}
.model-grid {
display: flex;
flex-direction: column;
gap: 16rpx;
}
.model-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx;
background: #f5f7fb;
border-radius: 12rpx;
border: 1rpx solid #eeeeee;
cursor: pointer;
transition: all 0.3s ease;
&:active {
background: #eef3ff;
}
.model-label {
font-size: 28rpx;
color: #232338;
font-weight: 500;
}
.model-value {
flex: 1;
text-align: right;
font-size: 26rpx;
color: #65686f;
margin: 0 16rpx;
}
:deep(.arrow-icon) {
font-size: 20rpx;
color: #9d9ea3;
}
}
.voice-settings {
display: flex;
flex-direction: column;
gap: 16rpx;
}
.voice-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx;
background: #f5f7fb;
border-radius: 12rpx;
border: 1rpx solid #eeeeee;
cursor: pointer;
transition: all 0.3s ease;
&:active {
background: #eef3ff;
}
.voice-label {
font-size: 28rpx;
color: #232338;
font-weight: 500;
}
.voice-value {
flex: 1;
text-align: right;
font-size: 26rpx;
color: #65686f;
margin: 0 16rpx;
}
:deep(.arrow-icon) {
font-size: 20rpx;
color: #9d9ea3;
}
}
.edit-functions {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx;
background: #f5f7fb;
border-radius: 12rpx;
border: 1rpx solid #eeeeee;
.function-dots {
display: flex;
gap: 12rpx;
.dot {
width: 16rpx;
height: 16rpx;
border-radius: 50%;
&.red {
background: #ff4d4f;
}
&.yellow {
background: #faad14;
}
&.green {
background: #52c41a;
}
}
}
.function-btn {
padding: 12rpx 24rpx;
background: rgba(51, 108, 255, 0.1);
color: #336cff;
border-radius: 20rpx;
font-size: 24rpx;
cursor: pointer;
transition: all 0.3s ease;
&:active {
background: #336cff;
color: white;
}
}
}
.save-section {
padding: 0;
margin-top: 40rpx;
:deep(.save-btn) {
width: 100%;
height: 80rpx;
border-radius: 16rpx;
font-size: 30rpx;
font-weight: 600;
background: #336cff;
&:active {
background: #2d5bd1;
}
}
}
</style>
@@ -244,7 +244,7 @@ onMounted(async () => {
</script> </script>
<template> <template>
<view class="page-container"> <view class="h-screen flex flex-col bg-[#f5f7fb]">
<!-- 头部导航 --> <!-- 头部导航 -->
<wd-navbar <wd-navbar
title="" title=""
@@ -261,7 +261,7 @@ onMounted(async () => {
<!-- 内容区域 --> <!-- 内容区域 -->
<scroll-view <scroll-view
scroll-y scroll-y
class="content-scroll-view box-border px-[20rpx]" class="flex-1 box-border px-[20rpx] bg-transparent"
:style="{ height: 'calc(100vh - 120rpx)' }" :style="{ height: 'calc(100vh - 120rpx)' }"
:scroll-with-animation="true" :scroll-with-animation="true"
> >
@@ -284,7 +284,7 @@ onMounted(async () => {
<!-- 未选插件 --> <!-- 未选插件 -->
<scroll-view <scroll-view
v-if="currentSegmented === '未选'" v-if="currentSegmented === '未选'"
class="plugin-scroll-view" class="max-h-[600rpx] bg-transparent"
scroll-y scroll-y
> >
<view <view
@@ -322,7 +322,7 @@ onMounted(async () => {
</scroll-view> </scroll-view>
<!-- 已选插件 --> <!-- 已选插件 -->
<scroll-view v-else class="plugin-scroll-view" scroll-y> <scroll-view v-else class="max-h-[600rpx] bg-transparent" scroll-y>
<view <view
v-if="selectedList.length === 0" v-if="selectedList.length === 0"
class="h-[400rpx] flex items-center justify-center" class="h-[400rpx] flex items-center justify-center"
@@ -398,7 +398,7 @@ onMounted(async () => {
</view> </view>
<!-- 工具列表 --> <!-- 工具列表 -->
<view class="mt-[20rpx] flex-1 overflow-hidden"> <view class="mt-[20rpx] flex-1 overflow-hidden">
<scroll-view class="plugin-scroll-view" scroll-y> <scroll-view class="max-h-[600rpx] bg-transparent" scroll-y>
<view <view
v-if="mcpTools && mcpTools.length === 0" v-if="mcpTools && mcpTools.length === 0"
class="h-[400rpx] flex items-center justify-center" class="h-[400rpx] flex items-center justify-center"
@@ -431,47 +431,48 @@ onMounted(async () => {
> >
<scroll-view <scroll-view
scroll-y scroll-y
class="param-scroll-container" class="bg-[#f5f7fb]"
:style="{ height: 'calc(75vh - 60rpx)' }" :style="{ height: 'calc(75vh - 60rpx)' }"
> >
<view class="param-content"> <view class="p-[30rpx] pb-[40rpx]">
<!-- 无参数提示 --> <!-- 无参数提示 -->
<view <view
v-if=" v-if="
!currentFunction?.fieldsMeta !currentFunction?.fieldsMeta
|| currentFunction.fieldsMeta.length === 0 || currentFunction.fieldsMeta.length === 0
" "
class="empty-params" class="h-[400rpx] flex items-center justify-center"
> >
<text class="empty-text"> <text class="text-[28rpx] text-[#999]">
{{ currentFunction?.name }} 无需配置参数 {{ currentFunction?.name }} 无需配置参数
</text> </text>
</view> </view>
<!-- 参数表单 - 卡片式布局 --> <!-- 参数表单 - 卡片式布局 -->
<view v-else class="param-cards"> <view v-else class="flex flex-col gap-[24rpx]">
<view <view
v-for="field in currentFunction.fieldsMeta" v-for="field in currentFunction.fieldsMeta"
:key="field.key" :key="field.key"
class="param-card" class="bg-white rounded-[20rpx] p-[30rpx] border border-[#eeeeee]"
style="box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);"
> >
<!-- 字段信息 --> <!-- 字段信息 -->
<view class="field-info"> <view class="mb-[24rpx]">
<text class="field-label"> <text class="block text-[32rpx] font-medium text-[#232338] mb-[8rpx]">
{{ field.label }} {{ field.label }}
</text> </text>
<text v-if="getFieldRemark(field)" class="field-desc"> <text v-if="getFieldRemark(field)" class="block text-[24rpx] text-[#65686f] leading-[1.5]">
{{ getFieldRemark(field) }} {{ getFieldRemark(field) }}
</text> </text>
</view> </view>
<!-- 输入控件 --> <!-- 输入控件 -->
<view class="field-input-container"> <view>
<!-- 字符串类型 --> <!-- 字符串类型 -->
<input <input
v-if="field.type === 'string'" v-if="field.type === 'string'"
v-model="tempParams[field.key]" v-model="tempParams[field.key]"
class="field-input" class="w-full h-[80rpx] p-[16rpx_20rpx] bg-[#f5f7fb] rounded-[12rpx] border border-[#eeeeee] text-[28rpx] text-[#232338] box-border focus:border-[#336cff] focus:bg-white placeholder:text-[#9d9ea3]"
type="text" type="text"
:placeholder="`请输入${field.label}`" :placeholder="`请输入${field.label}`"
@input=" @input="
@@ -480,13 +481,13 @@ onMounted(async () => {
> >
<!-- 数组类型 --> <!-- 数组类型 -->
<view v-else-if="field.type === 'array'" class="array-field"> <view v-else-if="field.type === 'array'">
<text class="field-hint"> <text class="block text-[24rpx] text-[#65686f] mb-[16rpx]">
每行输入一个项目 每行输入一个项目
</text> </text>
<textarea <textarea
v-model="arrayTextCache[field.key]" v-model="arrayTextCache[field.key]"
class="field-textarea" class="w-full min-h-[200rpx] p-[20rpx] bg-[#f5f7fb] rounded-[12rpx] border border-[#eeeeee] text-[26rpx] text-[#232338] leading-[1.6] box-border focus:border-[#336cff] focus:bg-white placeholder:text-[#9d9ea3]"
:placeholder="`请输入${field.label},每行一个`" :placeholder="`请输入${field.label},每行一个`"
@input=" @input="
handleArrayChange(field.key, $event.detail.value, field) handleArrayChange(field.key, $event.detail.value, field)
@@ -495,13 +496,13 @@ onMounted(async () => {
</view> </view>
<!-- JSON类型 --> <!-- JSON类型 -->
<view v-else-if="field.type === 'json'" class="json-field"> <view v-else-if="field.type === 'json'">
<text class="field-hint"> <text class="block text-[24rpx] text-[#65686f] mb-[16rpx]">
请输入有效的JSON格式 请输入有效的JSON格式
</text> </text>
<textarea <textarea
v-model="jsonTextCache[field.key]" v-model="jsonTextCache[field.key]"
class="field-textarea json-textarea" class="w-full min-h-[300rpx] p-[20rpx] bg-[#f5f7fb] rounded-[12rpx] border border-[#eeeeee] text-[26rpx] text-[#232338] leading-[1.6] box-border focus:border-[#336cff] focus:bg-white placeholder:text-[#9d9ea3] font-mono"
placeholder="请输入合法的JSON格式" placeholder="请输入合法的JSON格式"
@blur=" @blur="
handleJsonChange(field.key, $event.detail.value, field) handleJsonChange(field.key, $event.detail.value, field)
@@ -513,7 +514,7 @@ onMounted(async () => {
<input <input
v-else-if="field.type === 'number'" v-else-if="field.type === 'number'"
v-model="tempParams[field.key]" v-model="tempParams[field.key]"
class="field-input" class="w-full h-[80rpx] p-[16rpx_20rpx] bg-[#f5f7fb] rounded-[12rpx] border border-[#eeeeee] text-[28rpx] text-[#232338] box-border focus:border-[#336cff] focus:bg-white placeholder:text-[#9d9ea3]"
type="number" type="number"
:placeholder="`请输入${field.label}`" :placeholder="`请输入${field.label}`"
@input=" @input="
@@ -528,13 +529,13 @@ onMounted(async () => {
<!-- 布尔类型 --> <!-- 布尔类型 -->
<view <view
v-else-if="field.type === 'boolean' || field.type === 'bool'" v-else-if="field.type === 'boolean' || field.type === 'bool'"
class="switch-field" class="flex items-center justify-between py-[20rpx]"
> >
<view class="switch-info"> <view class="flex-1">
<text class="switch-label"> <text class="block text-[28rpx] text-[#232338] mb-[8rpx]">
启用功能 启用功能
</text> </text>
<text class="switch-desc"> <text class="block text-[24rpx] text-[#65686f]">
开启或关闭此功能 开启或关闭此功能
</text> </text>
</view> </view>
@@ -550,7 +551,7 @@ onMounted(async () => {
<input <input
v-else v-else
v-model="tempParams[field.key]" v-model="tempParams[field.key]"
class="field-input" class="w-full h-[80rpx] p-[16rpx_20rpx] bg-[#f5f7fb] rounded-[12rpx] border border-[#eeeeee] text-[28rpx] text-[#232338] box-border focus:border-[#336cff] focus:bg-white placeholder:text-[#9d9ea3]"
type="text" type="text"
:placeholder="`请输入${field.label}`" :placeholder="`请输入${field.label}`"
@input=" @input="
@@ -566,161 +567,3 @@ onMounted(async () => {
</view> </view>
</template> </template>
<style lang="scss" scoped>
.page-container {
display: flex;
flex-direction: column;
height: 100vh;
background: #f5f7fb;
}
.content-scroll-view {
flex: 1;
background: transparent;
}
// 插件列表滚动视图样式
.plugin-scroll-view {
max-height: 600rpx; // 最大高度为4个插件的高度
background: transparent;
}
// 参数编辑弹窗样式
.param-scroll-container {
background: #f5f7fb;
}
.param-content {
padding: 30rpx;
padding-bottom: 40rpx;
}
.empty-params {
display: flex;
justify-content: center;
align-items: center;
height: 400rpx;
.empty-text {
font-size: 28rpx;
color: #999;
}
}
.param-cards {
display: flex;
flex-direction: column;
gap: 24rpx;
}
.param-card {
background: #ffffff;
border-radius: 20rpx;
padding: 30rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
border: 1rpx solid #eeeeee;
}
.field-info {
margin-bottom: 24rpx;
.field-label {
display: block;
font-size: 32rpx;
font-weight: 500;
color: #232338;
margin-bottom: 8rpx;
}
.field-desc {
display: block;
font-size: 24rpx;
color: #65686f;
line-height: 1.5;
}
}
.field-input-container {
.field-input {
width: 100%;
padding: 16rpx 20rpx;
height: 80rpx;
background: #f5f7fb;
border-radius: 12rpx;
border: 1rpx solid #eeeeee;
font-size: 28rpx;
color: #232338;
box-sizing: border-box;
&:focus {
border-color: #336cff;
background: #ffffff;
}
&::placeholder {
color: #9d9ea3;
}
}
.field-textarea {
width: 100%;
min-height: 200rpx;
padding: 20rpx;
background: #f5f7fb;
border-radius: 12rpx;
border: 1rpx solid #eeeeee;
font-size: 26rpx;
color: #232338;
line-height: 1.6;
box-sizing: border-box;
&:focus {
border-color: #336cff;
background: #ffffff;
}
&.json-textarea {
min-height: 300rpx;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
}
&::placeholder {
color: #9d9ea3;
}
}
}
.array-field,
.json-field {
.field-hint {
display: block;
font-size: 24rpx;
color: #65686f;
margin-bottom: 16rpx;
}
}
.switch-field {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 0;
.switch-info {
flex: 1;
.switch-label {
display: block;
font-size: 28rpx;
color: #232338;
margin-bottom: 8rpx;
}
.switch-desc {
display: block;
font-size: 24rpx;
color: #65686f;
}
}
}
</style>