Compare commits

...

2 Commits

Author SHA1 Message Date
cafe7b0d99 Merge branch 'main' of http://18.167.251.121:10003/aidlab/FiDA_Front 2026-02-06 09:43:10 +08:00
d323abb57e feat: 主页Input 2026-02-06 09:35:46 +08:00
8 changed files with 676 additions and 299 deletions

View File

@@ -38,4 +38,8 @@ body,
background-color: rgba(248, 247, 245, 1);
background-image: url('@/assets/images/home-bg.png');
background-size: 100% 100%;
}
.flex{
display: flex;
}

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.29289 7.29289C7.68342 6.90237 8.31658 6.90237 8.70711 7.29289L12 10.5858L15.2929 7.29289C15.6834 6.90237 16.3166 6.90237 16.7071 7.29289C17.0976 7.68342 17.0976 8.31658 16.7071 8.70711L13.4142 12L16.7071 15.2929C17.0976 15.6834 17.0976 16.3166 16.7071 16.7071C16.3166 17.0976 15.6834 17.0976 15.2929 16.7071L12 13.4142L8.70711 16.7071C8.31658 17.0976 7.68342 17.0976 7.29289 16.7071C6.90237 16.3166 6.90237 15.6834 7.29289 15.2929L10.5858 12L7.29289 8.70711C6.90237 8.31658 6.90237 7.68342 7.29289 7.29289Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 645 B

View File

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.03431 5.03431C5.34673 4.7219 5.85327 4.7219 6.16569 5.03431L8 6.86863L9.83432 5.03431C10.1467 4.7219 10.6533 4.7219 10.9657 5.03431C11.2781 5.34673 11.2781 5.85327 10.9657 6.16569L9.13137 8L10.9657 9.83432C11.2781 10.1467 11.2781 10.6533 10.9657 10.9657C10.6533 11.2781 10.1467 11.2781 9.83432 10.9657L8 9.13137L6.16569 10.9657C5.85327 11.2781 5.34673 11.2781 5.03431 10.9657C4.7219 10.6533 4.7219 10.1467 5.03431 9.83432L6.86863 8L5.03431 6.16569C4.7219 5.85327 4.7219 5.34673 5.03431 5.03431Z" fill="#CDCDCD"/>
<path d="M8 1.6C4.46538 1.6 1.6 4.46538 1.6 8C1.6 11.5346 4.46538 14.4 8 14.4C11.5346 14.4 14.4 11.5346 14.4 8C14.4 4.46538 11.5346 1.6 8 1.6ZM0 8C0 3.58172 3.58172 0 8 0C12.4183 0 16 3.58172 16 8C16 12.4183 12.4183 16 8 16C3.58172 16 0 12.4183 0 8Z" fill="#CDCDCD"/>
</svg>

After

Width:  |  Height:  |  Size: 896 B

View File

@@ -0,0 +1,3 @@
<svg width="16" height="20" viewBox="0 0 16 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.7248 18.6371C11.7248 18.8138 11.6476 18.9906 11.5262 19.1231C11.3937 19.2445 11.2168 19.3217 11.0402 19.3217H4.14022C3.95255 19.3217 3.77596 19.2445 3.64348 19.1231C3.52204 18.9906 3.44484 18.8138 3.44484 18.6371C3.44484 18.4495 3.52204 18.2729 3.64348 18.1404C3.77596 18.019 3.95255 17.9418 4.14022 17.9418H11.0402C11.2168 17.9418 11.3937 18.019 11.5262 18.1404C11.6476 18.2729 11.7248 18.4495 11.7248 18.6371ZM15.1802 7.59719C15.1802 8.74535 14.9153 9.88246 14.4185 10.9092C13.9106 11.9469 13.1821 12.8524 12.2768 13.5589C12.1002 13.6914 11.9675 13.8569 11.8682 14.0446C11.7799 14.2433 11.7248 14.4532 11.7248 14.6629V15.1818C11.7248 15.5461 11.5811 15.8992 11.3162 16.1532C11.0623 16.4181 10.7091 16.5618 10.3448 16.5618H4.82483C4.46051 16.5618 4.10736 16.4181 3.85345 16.1532C3.58849 15.8992 3.44484 15.5461 3.44484 15.1818V14.6629C3.44484 14.4532 3.40081 14.2433 3.30145 14.0557C3.21313 13.868 3.06949 13.7025 2.90389 13.57C1.99862 12.8634 1.27004 11.969 0.762207 10.9423C0.265409 9.91562 0.000245425 8.7785 0.000245425 7.63035C-0.0328744 3.52349 3.29036 0.1012 7.39722 0.00184018C8.4129 -0.0202397 9.41747 0.156378 10.3669 0.531736C11.3053 0.896054 12.1664 1.44802 12.884 2.15458C13.6127 2.86113 14.1868 3.71122 14.5842 4.64961C14.9706 5.57697 15.1802 6.58152 15.1802 7.59719ZM13.8002 7.59719C13.8002 6.75816 13.6346 5.94127 13.3034 5.17951C12.9833 4.41775 12.5196 3.72241 11.9234 3.14833C11.3273 2.56322 10.6319 2.11035 9.85911 1.81227C9.08632 1.50315 8.25837 1.35975 7.43038 1.38183C4.07423 1.45911 1.35816 4.26316 1.38024 7.63035C1.38024 8.5577 1.59007 9.48526 2.00959 10.3243C2.41807 11.1744 3.01431 11.903 3.75399 12.477C4.09623 12.742 4.36115 13.0731 4.54883 13.4484C4.72547 13.8238 4.82483 14.2434 4.82483 14.6629V15.1818H6.90021V11.3288L4.33914 8.76749C4.20666 8.63501 4.12944 8.46948 4.12944 8.2818C4.12944 8.09412 4.20666 7.92859 4.33914 7.79611C4.47162 7.66363 4.63715 7.58641 4.82483 7.58641C5.01251 7.58641 5.18908 7.66363 5.31052 7.79611L7.58482 10.0704L9.85911 7.79611C9.92535 7.72987 9.99154 7.67452 10.0799 7.6414C10.1682 7.60828 10.2565 7.58641 10.3448 7.58641C10.4331 7.58641 10.5214 7.60828 10.6098 7.6414C10.6981 7.67452 10.7643 7.72987 10.8305 7.79611C10.8967 7.86235 10.9521 7.92853 10.9852 8.01685C11.0183 8.10517 11.0402 8.19348 11.0402 8.2818C11.0402 8.37012 11.0183 8.45843 10.9852 8.54675C10.9521 8.63507 10.8967 8.70125 10.8305 8.76749L8.2802 11.3288V15.1818H10.3448V14.6629C10.3448 14.2434 10.4442 13.8238 10.6319 13.4484C10.8195 13.062 11.0844 12.731 11.4156 12.477C12.1664 11.903 12.7626 11.1523 13.1711 10.3132C13.5796 9.46317 13.8002 8.53559 13.8002 7.59719Z" fill="#FFDB56"/>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 B

View File

@@ -1,112 +1,115 @@
export default {
Login: {
login: 'Log in',
register: 'Register',
signUp: 'Sign up',
loginTo: 'Log on to',
loginTitle: 'A multi-agent canvas for rapid, trend driven design iteration.',
name: 'Name',
email: 'Email',
password: 'Password',
enterName: 'Enter your name',
enterEmail: 'Enter your email',
enterPassword: 'Enter your password',
forgetPassword: 'Forget password?',
pleaseInputName: 'Please input the name',
nameLengthError: 'Name length must be between {min} and {max} characters',
pleaseInputEmail: 'Please input the email',
emailFormatError: 'Please input the email again',
pleaseInputPassword: 'Please input the password',
passwordLengthError: 'Password length must be between {min} and {max} characters',
pleaseTermsPolicy: 'Please agree to the Terms, Policy and Fees',
agreeTermsPolicy:
'I agree to the <span onclick="onClickPrivacy()">Terms, Policy</span> and Fees.',
noAccountToSignUp: `Don't have an account? <span onclick="onClickRegister()">Sign up</span>`,
registerFor: 'Register for',
registerTip: 'A multi-agent canvas for rapid, trend driven design iteration.',
havenAccountToLogin: `Already have an account? <span onclick="onClickLogin()">Log in</span>`,
verifyEmail: 'Verify your email address',
verifyCodeHasSent: 'A verification code has been sent to <span>{email}</span>',
verify: 'Verify',
resendCode: 'Resend Code',
resendCodeIn: 'Resend Code in {time}',
orContinueWith: 'or continue with',
googleLogin: 'Sign in with Google',
wechatLogin: 'Sign in with Wechat'
Login: {
login: 'Log in',
register: 'Register',
signUp: 'Sign up',
loginTo: 'Log on to',
loginTitle: 'A multi-agent canvas for rapid, trend driven design iteration.',
name: 'Name',
email: 'Email',
password: 'Password',
enterName: 'Enter your name',
enterEmail: 'Enter your email',
enterPassword: 'Enter your password',
forgetPassword: 'Forget password?',
pleaseInputName: 'Please input the name',
nameLengthError: 'Name length must be between {min} and {max} characters',
pleaseInputEmail: 'Please input the email',
emailFormatError: 'Please input the email again',
pleaseInputPassword: 'Please input the password',
passwordLengthError: 'Password length must be between {min} and {max} characters',
pleaseTermsPolicy: 'Please agree to the Terms, Policy and Fees',
agreeTermsPolicy:
'I agree to the <span onclick="onClickPrivacy()">Terms, Policy</span> and Fees.',
noAccountToSignUp: `Don't have an account? <span onclick="onClickRegister()">Sign up</span>`,
registerFor: 'Register for',
registerTip: 'A multi-agent canvas for rapid, trend driven design iteration.',
havenAccountToLogin: `Already have an account? <span onclick="onClickLogin()">Log in</span>`,
verifyEmail: 'Verify your email address',
verifyCodeHasSent: 'A verification code has been sent to <span>{email}</span>',
verify: 'Verify',
resendCode: 'Resend Code',
resendCodeIn: 'Resend Code in {time}',
orContinueWith: 'or continue with',
googleLogin: 'Sign in with Google',
wechatLogin: 'Sign in with Wechat'
},
Nuic: {
hiName: 'Hi, {name}.',
nuic1Title: `Help Fiphant discover the <b>'YOU'</b> in your space.`,
nuic1Tip: `Let's set up your profile. A few quick details will help Fiphant understand<br />your needs and find exactly what you're looking for.`,
letsGo: 'Lets go, Fiphant!',
skip: 'Skip',
next: 'Next',
nuic2Title: `What's your dream <b>home vibe</b> ?`,
loadMore: 'Load more',
nuic3Title: `<b>Where</b> are you based? What do you <b>do</b> ?`,
basedIn: 'Based in',
role: 'Role',
allSet: 'All set!'
},
Home: {
creditsNum: 'Credits: {num}',
newProject: 'New Project',
home: 'Home',
history: 'History',
today: 'Today',
yesterday: 'Yesterday',
earlierChat: 'Earlier Chat'
},
Input: {
placeholder: 'Please input',
selectPlaceholder: 'Please select',
typePlaceholder: 'Type',
areaPlaceholder: 'Region',
stylePlaceholder: 'Style',
types: {
sofa: 'Sofa',
desk: 'Desk',
chair: 'Chair'
},
Nuic: {
hiName: 'Hi, {name}.',
nuic1Title: `Help Fiphant discover the <b>'YOU'</b> in your space.`,
nuic1Tip: `Let's set up your profile. A few quick details will help Fiphant understand<br />your needs and find exactly what you're looking for.`,
letsGo: 'Lets go, Fiphant!',
skip: 'Skip',
next: 'Next',
nuic2Title: `What's your dream <b>home vibe</b> ?`,
loadMore: 'Load more',
nuic3Title: `<b>Where</b> are you based? What do you <b>do</b> ?`,
basedIn: 'Based in',
role: 'Role',
allSet: 'All set!'
styles: {
Coastal: 'Coastal',
Verdant: 'Verdant',
Traditional: 'Traditional',
CenturyChrome: 'Century\nChrome',
ModernRevival: 'Modern\nRevival',
Tuscan2000s: "Tuscan\n2000's",
Bauhaus: 'Bauhaus',
Constructivism: 'Constructivism',
NordicNoir: 'Nordic\nNoir'
},
Home: {
creditsNum: 'Credits: {num}',
newProject: 'New Project',
home: 'Home',
history: 'History',
today: 'Today',
yesterday: 'Yesterday',
earlierChat: 'Earlier Chat'
},
Input: {
placeholder: 'Please input',
selectPlaceholder: 'Please select',
typePlaceholder: 'Type',
areaPlaceholder: 'Region',
stylePlaceholder: 'Style',
types: {
sofa: 'Sofa',
desk: 'Desk',
chair: 'Chair'
},
styles: {
Coastal: 'Coastal',
Verdant: 'Verdant',
Traditional: 'Traditional',
CenturyChrome: 'Century\nChrome',
ModernRevival: 'Modern\nRevival',
Tuscan2000s: "Tuscan\n2000's",
Bauhaus: 'Bauhaus',
Constructivism: 'Constructivism',
NordicNoir: 'Nordic\nNoir'
},
chooseStyle: 'Choose Style',
setting: 'Setting',
settingOptions: {
creativity: 'Creativity',
diversity: 'Diversity',
relevance: 'Relevance'
},
confirm: 'Confirm'
},
area: {
unitedStates: 'United States',
singapore: 'Singapore',
australia: 'Australia',
southKorea: 'South Korea',
china: 'China',
italy: 'Italy',
france: 'France',
japan: 'Japan',
canada: 'Canada',
germany: 'Germany'
chooseStyle: 'Choose Style',
setting: 'Setting',
settingOptions: {
creativity: 'Creativity',
diversity: 'Diversity',
relevance: 'Relevance'
},
confirm: 'Confirm',
styleTitle: 'Settings',
createProject: 'Create Project',
trendingReport: 'Trending Report'
},
area: {
unitedStates: 'United States',
singapore: 'Singapore',
australia: 'Australia',
southKorea: 'South Korea',
china: 'China',
italy: 'Italy',
france: 'France',
japan: 'Japan',
canada: 'Canada',
germany: 'Germany'
},
// Version Tree
VersionTree: {
versionInformation: 'Version Information',
input: 'Input',
userRequest: 'User Request',
sketch: 'Sketch',
generateResult: 'Generate Result'
}
// Version Tree
VersionTree: {
versionInformation: 'Version Information',
input: 'Input',
userRequest: 'User Request',
sketch: 'Sketch',
generateResult: 'Generate Result'
}
}

View File

@@ -1,96 +1,99 @@
export default {
Login: {
login: '登录',
register: '注册',
signUp: '注册',
loginTo: '登录到',
loginTitle: '一个多智能体画布,用于快速、趋势驱动的设计迭代。',
name: '姓名',
email: '邮箱',
password: '密码',
enterName: '请输入姓名',
enterEmail: '请输入邮箱',
enterPassword: '请输入密码',
forgetPassword: '忘记密码?',
pleaseInputName: '请输入姓名',
nameLengthError: '姓名长度必须在 {min} 到 {max} 个字符之间',
pleaseInputEmail: '请输入邮箱',
emailFormatError: '请输入正确的邮箱',
pleaseInputPassword: '请输入密码',
passwordLengthError: '密码长度必须在 {min} 到 {max} 个字符之间',
pleaseTermsPolicy: '请同意条款、政策和费用',
agreeTermsPolicy: '我同意 <span onclick="onClickPrivacy()">条款、政策</span> 和费用。',
noAccountToSignUp: `还没有账号? <span onclick="onClickRegister()">注册</span>`,
registerFor: '注册账号',
registerTip: '一个多智能体画布,用于快速、趋势驱动的设计迭代。',
havenAccountToLogin: `已经有账号? <span onclick="onClickLogin()">登录</span>`,
verifyEmail: '验证您的邮箱地址',
verifyCodeHasSent: '已发送验证码到 <span>{email}</span>',
verifyCode: '请输入验证码',
verify: '验证',
resendCode: '重新发送验证码',
resendCodeIn: '重新发送验证码倒计时 {time}',
orContinueWith: '或者使用',
googleLogin: '使用 Google 登录',
wechatLogin: '使用微信登录',
},
Nuic: {
hiName: '你好,{name}。',
nuic1Title: `帮助 Fiphant 发现您空间中的 <b>'YOU'</b>。`,
nuic1Tip: `让我们设置您的个人资料。几个快速的细节将帮助 Fiphant 理解您的需求并找到您正在寻找的内容。`,
letsGo: '让我们开始Fiphant',
skip: '跳过',
next: '下一步',
nuic2Title: `您理想中 <b>家的氛围</b> 是什么?`,
loadMore: '加载更多',
nuic3Title: `您在 <b>哪里</b> 工作?您从事什么 <b>工作</b> `,
basedIn: '公司',
role: '角色',
allSet: '准备好了!',
},
Home: {
creditsNum: '积分: {num}',
newProject: '新建项目',
home: '首页',
history: '历史记录',
today: '今天',
yesterday: '昨天',
earlierChat: '更早的',
},
Input: {
placeholder: '请输入',
selectPlaceholder: '请选择',
type: '类型',
area: '地区',
style: '风格',
types: {
sofa: '沙发',
desk: '书桌',
chair: '椅子'
},
styles: {
modern: '现代',
classic: '古典'
},
chooseStyle: '选择风格',
setting: 'Setting',
settingOptions: {
creativity: '创意度',
diversity: '多样性',
relevance: '相关度'
},
confirm: '确认'
},
area: {
unitedStates: '美国',
singapore: '新加坡',
australia: '澳大利亚',
southKorea: '国',
china: '中国',
italy: '大利',
france: '国',
japan: '日本',
canada: '加拿大',
germany: '国'
}
Login: {
login: '登录',
register: '注册',
signUp: '注册',
loginTo: '登录到',
loginTitle: '一个多智能体画布,用于快速、趋势驱动的设计迭代。',
name: '姓名',
email: '邮箱',
password: '密码',
enterName: '请输入姓名',
enterEmail: '请输入邮箱',
enterPassword: '请输入密码',
forgetPassword: '忘记密码?',
pleaseInputName: '请输入姓名',
nameLengthError: '姓名长度必须在 {min} 到 {max} 个字符之间',
pleaseInputEmail: '请输入邮箱',
emailFormatError: '请输入正确的邮箱',
pleaseInputPassword: '请输入密码',
passwordLengthError: '密码长度必须在 {min} 到 {max} 个字符之间',
pleaseTermsPolicy: '请同意条款、政策和费用',
agreeTermsPolicy: '我同意 <span onclick="onClickPrivacy()">条款、政策</span> 和费用。',
noAccountToSignUp: `还没有账号? <span onclick="onClickRegister()">注册</span>`,
registerFor: '注册账号',
registerTip: '一个多智能体画布,用于快速、趋势驱动的设计迭代。',
havenAccountToLogin: `已经有账号? <span onclick="onClickLogin()">登录</span>`,
verifyEmail: '验证您的邮箱地址',
verifyCodeHasSent: '已发送验证码到 <span>{email}</span>',
verifyCode: '请输入验证码',
verify: '验证',
resendCode: '重新发送验证码',
resendCodeIn: '重新发送验证码倒计时 {time}',
orContinueWith: '或者使用',
googleLogin: '使用 Google 登录',
wechatLogin: '使用微信登录'
},
Nuic: {
hiName: '你好,{name}。',
nuic1Title: `帮助 Fiphant 发现您空间中的 <b>'YOU'</b>。`,
nuic1Tip: `让我们设置您的个人资料。几个快速的细节将帮助 Fiphant 理解您的需求并找到您正在寻找的内容。`,
letsGo: '让我们开始Fiphant',
skip: '跳过',
next: '下一步',
nuic2Title: `您理想中 <b>家的氛围</b> 是什么?`,
loadMore: '加载更多',
nuic3Title: `您在 <b>哪里</b> 工作?您从事什么 <b>工作</b> `,
basedIn: '公司',
role: '角色',
allSet: '准备好了!'
},
Home: {
creditsNum: '积分: {num}',
newProject: '新建项目',
home: '首页',
history: '历史记录',
today: '今天',
yesterday: '昨天',
earlierChat: '更早的'
},
Input: {
placeholder: '请输入',
selectPlaceholder: '请选择',
type: '类型',
area: '地区',
style: '风格',
types: {
sofa: '沙发',
desk: '书桌',
chair: '椅子'
},
styles: {
modern: '现代',
classic: '古典'
},
chooseStyle: '选择风格',
setting: 'Setting',
settingOptions: {
creativity: '创意度',
diversity: '多样性',
relevance: '相关度'
},
confirm: '确认',
styleTitle: '设置',
createProject: '创建项目',
trendingReport: '趋势报告'
},
area: {
unitedStates: '国',
singapore: '新加坡',
australia: '大利',
southKorea: '国',
china: '中国',
italy: '意大利',
france: '国',
japan: '日本',
canada: '加拿大',
germany: '德国'
}
}

View File

@@ -1,103 +1,200 @@
<template>
<div class="assist-input-wrapper flex flex-col">
<textarea
class="input"
type="text"
v-model="inputValue"
:placeholder="$t('Input.placeholder')"
/>
<div class="operate flex align-center">
<div class="attach flex flex-center">
<img src="@/assets/icons/attach.svg" alt="" />
<div class="scroll-content flex-col">
<!-- 图片预览区域 -->
<div v-if="uploadedImages.length > 0" class="image-preview-list flex wrap">
<div v-for="(image, index) in uploadedImages" :key="index" class="image-preview-item">
<img :src="image.url" :alt="image.name" class="preview-image" />
<div class="image-remove-btn" @click="removeImage(index)">
<SvgIcon name="delete" size="16" />
</div>
</div>
</div>
<el-select v-model="typeValue" :placeholder="$t('Input.typePlaceholder')">
<el-option
v-for="item in typeOptions"
class="input-option"
:key="item.value"
:label="$t(item.label)"
:value="item.value"
/>
</el-select>
<el-select v-model="areaValue" :placeholder="$t('Input.areaPlaceholder')">
<el-option
v-for="item in areaOptions"
class="input-option"
:key="item.value"
:label="$t(item.label)"
:value="item.value"
/>
</el-select>
<div class="fida-style-select-wrapper">
<el-select
v-model="styleValue"
:placeholder="$t('Input.stylePlaceholder')"
@focus="openStylePopup"
<!-- 编辑区域 -->
<div
ref="editorRef"
class="editor"
contenteditable="true"
:placeholder="$t('Input.placeholder')"
@input="handleEditorInput"
@keydown="handleEditorKeydown"
@paste="handleEditorPaste"
>
<!-- <Tag v-if="showReportTag" /> -->
<div class="editor-tag report-btn flex-center" v-if="showReportTag" contenteditable="false">
<SvgIcon class="light-icon" name="light" size="16" />
<span>{{ $t('Input.trendingReport') }}</span>
<SvgIcon
class="close-icon"
name="closeTransparent"
size="24"
color="#e6e6e6"
@click="showReportTag = false"
/>
</div>
</div>
</div>
<div class="operate flex space-between">
<div class="left flex align-center">
<div class="attach flex flex-center" @click="triggerFileUpload">
<img src="@/assets/icons/attach.svg" alt="" />
</div>
<input
ref="fileInputRef"
type="file"
accept="image/*"
style="display: none"
@change="handleFileChange"
/>
<el-select v-model="typeValue" :placeholder="$t('Input.typePlaceholder')">
<el-option
v-for="item in typeOptions"
class="input-option"
:key="item.value"
:label="$t(item.label)"
:value="item.value"
/>
</el-select>
<el-select v-model="areaValue" :placeholder="$t('Input.areaPlaceholder')">
<el-option
v-for="item in areaOptions"
class="input-option"
:key="item.value"
:label="$t(item.label)"
:value="item.value"
/>
</el-select>
<div class="fida-style-select-wrapper">
<el-select
v-model="styleValue"
:placeholder="$t('Input.stylePlaceholder')"
@focus="openStylePopup"
/>
<el-popover
v-model:visible="stylePopupVisible"
placement="top"
:width="342"
:show-arrow="false"
trigger="click"
popper-class="fida-style-select-popover"
>
<template #reference>
<div class="fida-style-select-trigger"></div>
</template>
<div class="fida-style-popover-content flex flex-col">
<div class="fida-style-popover-header">{{ $t('Input.chooseStyle') }}</div>
<div class="fida-style-popover-grid">
<div
v-for="item in styleOptions"
:key="item.value"
class="fida-style-popover-item"
:class="{ 'is-selected': tempSelectedValue === item.value }"
@click="selectStyle(item.value)"
>
<span class="fida-option-label">{{ $t(item.label) }}</span>
</div>
</div>
<div class="fida-style-popover-footer flex flex-center">
<button class="fida-confirm-btn" @click="confirmStyle">
{{ $t('Input.confirm') }}
</button>
</div>
</div>
</el-popover>
</div>
<el-popover
v-model:visible="stylePopupVisible"
v-model:visible="settingPopupVisible"
placement="top"
:width="342"
:show-arrow="false"
trigger="click"
popper-class="fida-style-select-popover"
popper-class="fida-setting-popover"
>
<template #reference>
<div class="fida-style-select-trigger"></div>
<img src="@/assets/images/setting.png" class="setting-icon" />
</template>
<div class="fida-style-popover-content flex flex-col">
<div class="fida-style-popover-header">{{ $t('Input.chooseStyle') }}</div>
<div class="fida-style-popover-grid">
<div class="fida-setting-popover-content flex flex-col">
<div class="fida-setting-popover-header">{{ $t('Input.styleTitle') }}</div>
<div class="fida-setting-slider-list">
<div
v-for="item in styleOptions"
:key="item.value"
class="fida-style-popover-item"
:class="{ 'is-selected': tempSelectedValue === item.value }"
@click="selectStyle(item.value)"
v-for="item in settingOptions"
:key="item.label"
class="fida-setting-slider-item"
>
<span class="fida-option-label">{{ $t(item.label) }}</span>
<div class="fida-slider-label">{{ $t(item.label) }}</div>
<div class="fida-slider-row flex align-center">
<el-slider
class="setting-popover-slider"
v-model="item.value"
:show-tooltip="false"
/>
<span class="fida-slider-value">{{ item.value }}%</span>
</div>
</div>
</div>
<div class="fida-style-popover-footer flex flex-center">
<button class="fida-confirm-btn" @click="confirmStyle">
{{ $t('Input.confirm') }}
</button>
</div>
</div>
</el-popover>
</div>
<el-popover
v-model:visible="settingPopupVisible"
placement="top"
:width="342"
:show-arrow="false"
trigger="click"
popper-class="fida-setting-popover"
>
<template #reference>
<img src="@/assets/images/setting.png" class="setting-icon" />
</template>
<div class="fida-setting-popover-content flex flex-col">
<div class="fida-setting-popover-header">{{ $t('Input.setting') }}</div>
<div class="fida-setting-slider-list">
<div v-for="item in settingOptions" :key="item.label" class="fida-setting-slider-item">
<div class="fida-slider-label">{{ $t(item.label) }}</div>
<div class="fida-slider-row flex align-center">
<el-slider v-model="item.value" :show-tooltip="false" />
<span class="fida-slider-value">{{ item.value }}%</span>
</div>
</div>
</div>
<div class="right">
<div class="create-btn flex flex-center">
<img src="@/assets/images/shining.png" class="shining-icon" alt="" />
<span class="create-btn-text">{{ $t('Input.createProject') }}</span>
</div>
</el-popover>
</div>
</div>
<div class="report-btn flex flex-center" @click="toogltReportTag">
<SvgIcon class="light-icon" name="light" size="16" />
<span>{{ $t('Input.trendingReport') }}</span>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { computed, ref, watch, nextTick, onMounted } from 'vue'
import { areaList } from '@/utils/area'
import { useI18n } from 'vue-i18n'
// import Tag from './Tag.vue'
const { t } = useI18n()
// 图片上传相关
const fileInputRef = ref<HTMLInputElement | null>(null)
const uploadedImages = ref<Array<{ url: string; name: string }>>([])
// 触发文件上传
const triggerFileUpload = () => {
fileInputRef.value?.click()
}
// TODO 标签被删除后无法重新出现
// 处理文件选择
const handleFileChange = (event: Event) => {
const input = event.target as HTMLInputElement
if (input.files) {
Array.from(input.files).forEach((file) => {
// 只处理图片文件
if (file.type.startsWith('image/')) {
const reader = new FileReader()
reader.onload = (e) => {
uploadedImages.value.push({
url: e.target?.result as string,
name: file.name
})
}
reader.readAsDataURL(file)
}
})
}
// 清空input的value允许重复选择同一文件
input.value = ''
}
// 移除图片
const removeImage = (index: number) => {
uploadedImages.value.splice(index, 1)
}
const styleKeys: string[] = [
'Coastal',
@@ -111,8 +208,97 @@ const styleKeys: string[] = [
'NordicNoir'
]
// 标签相关固定标签v-show 控制显示)
const showReportTag = ref(false)
const editorRef = ref<HTMLDivElement | null>(null)
const inputValue = ref<string>('')
const toogltReportTag = () => {
console.log(showReportTag.value)
showReportTag.value = !showReportTag.value
}
const handleEditorInput = () => {
if (!editorRef.value) return
// 提取纯文本(排除标签)
let text = ''
const walker = document.createTreeWalker(editorRef.value, NodeFilter.SHOW_TEXT, null)
let node: Node | null
while ((node = walker.nextNode())) {
text += node.textContent
}
// 移除末尾的空格(如果有的话)
text = text.replace(/\s+$/, '')
inputValue.value = text
// 自动调整高度
autoResizeEditor()
}
const handleEditorKeydown = (e: KeyboardEvent) => {
// if (e.key === 'Backspace') {
// const selection = window.getSelection()
// if (selection && selection.rangeCount > 0) {
// const range = selection.getRangeAt(0)
// if (range.collapsed) {
// const node = range.startContainer
// const offset = range.startOffset
// // 如果光标在文本节点开头,且前一个兄弟是标签
// if (
// offset === 0 &&
// node.nodeType === Node.TEXT_NODE &&
// node.previousSibling &&
// (node.previousSibling as HTMLElement).classList.contains('editor-tag')
// ) {
// e.preventDefault()
// nextTick(() => (showReportTag.value = false))
// }
// // 如果光标在编辑器开头,且第一个子节点是标签
// else if (
// offset === 0 &&
// node === editorRef.value &&
// editorRef.value.firstChild &&
// (editorRef.value.firstChild as HTMLElement).classList.contains('editor-tag')
// ) {
// e.preventDefault()
// nextTick(() => (showReportTag.value = false))
// }
// }
// }
// }
}
const handleEditorPaste = (e: ClipboardEvent) => {
e.preventDefault()
const text = e.clipboardData?.getData('text/plain') || ''
document.execCommand('insertText', false, text)
}
const autoResizeEditor = () => {
const editor = editorRef.value
if (editor) {
editor.style.height = 'auto'
const maxHeight = 20 * parseFloat(getComputedStyle(document.documentElement).fontSize || '16')
editor.style.height = Math.min(editor.scrollHeight, maxHeight) + 'px'
}
}
// 监听 inputValue 外部变化
watch(inputValue, () => {
nextTick(() => {
autoResizeEditor()
})
})
// 初始化编辑器高度
onMounted(() => {
autoResizeEditor()
})
const typeValue = ref<string>('')
const areaValue = ref<string>('')
const styleValue = ref<string>('')
@@ -170,32 +356,158 @@ const styleOptions = ref<any[]>(
<style lang="less" scoped>
.assist-input-wrapper {
height: 23.5rem;
min-height: 23.5rem;
max-height: 43.5rem;
width: 106.3rem;
border-radius: 2.8rem;
background-color: #fff;
border: 0.1rem solid #00000005;
box-shadow: 0px 5px 14px 0px #0000001a;
margin: 0 auto;
padding: 3.4rem 1.7rem 1.7rem 2rem;
.input {
flex: 1;
padding: 0;
position: relative;
.report-btn {
position: absolute;
bottom: -7.4rem;
height: 4.4rem;
border-radius: 2.2rem;
width: 20rem;
background-color: #fff;
border: 0.11rem solid #f6f4ef1a;
column-gap: 1.2rem;
cursor: pointer;
.c-svg {
width: 1.5rem;
height: 1.9rem;
}
}
.scroll-content {
overflow-y: visible;
padding: 3.4rem 1.7rem 1.7rem;
}
.editor {
width: 100%;
min-height: 5rem;
max-height: 20rem;
border: none;
outline: none;
padding: 0 1.4rem;
padding: 0 1.4rem 1.4rem;
font-size: 2rem;
font-family: 'InterRegular';
font-weight: 400;
color: #000000;
resize: none;
overflow-y: auto;
overflow-x: hidden;
line-height: 1.5;
white-space: pre-wrap;
word-wrap: break-word;
// 占位符
&:empty::before {
content: attr(placeholder);
color: #999;
pointer-events: none;
}
// 标签样式
.editor-tag {
width: 21.8rem;
height: 4.4rem;
display: inline-flex;
position: initial;
bottom: initial;
border: 0.11rem solid #0000001a;
font-family: 'GeneralMedium';
font-weight: 500;
font-size: 1.8rem;
column-gap: 0;
span {
margin: 0 0.7rem 0 1.2rem;
}
.c-svg.close-icon {
width: 2.4rem;
height: 2.4rem;
cursor: pointer;
}
}
}
// 标签容器(已废弃,保留兼容性)
.tags-container {
display: inline-flex;
flex-wrap: wrap;
gap: 0.5rem;
padding: 0 1.4rem 0.5rem;
.tag-item {
display: inline-flex;
align-items: center;
}
}
// 图片预览区域样式
.image-preview-list {
padding: 0 1.4rem 1rem;
column-gap: 1rem;
max-height: 15rem;
overflow-y: auto;
flex-shrink: 0;
.image-preview-item {
position: relative;
width: 8.6rem;
height: 8.6rem;
border-radius: 1.5rem;
overflow: hidden;
flex-shrink: 0;
border: 0.1rem solid #cdcdcd;
.preview-image {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 0.8rem;
}
.image-remove-btn {
position: absolute;
top: 0.2rem;
right: 0.2rem;
width: 1.6rem;
height: 1.6rem;
color: #fff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
cursor: pointer;
opacity: 0;
transition: opacity 0.2s ease;
}
&:hover .image-remove-btn {
opacity: 1;
}
}
}
.operate {
column-gap: 2rem;
flex-shrink: 0;
margin-top: auto;
padding: 0 1.7rem 1.7rem;
.left {
column-gap: 2rem;
}
.attach {
width: 4rem;
height: 4rem;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 50%;
cursor: pointer;
}
.el-select {
width: 13.9rem;
@@ -235,6 +547,21 @@ const styleOptions = ref<any[]>(
height: 2.4rem;
cursor: pointer;
}
.create-btn {
background-color: #ff7a51;
height: 4rem;
width: 13rem;
color: #fff;
border-radius: 4.2rem;
font-family: 'Mazzard';
font-weight: 600;
font-size: 1.28rem;
cursor: pointer;
.shining-icon {
width: 1.4rem;
height: 1.4rem;
}
}
}
}
.input-option {
@@ -336,12 +663,15 @@ const styleOptions = ref<any[]>(
}
.fida-setting-popover {
width: 34.2rem !important;
padding: 0 !important;
border-radius: 0.6rem !important;
box-shadow: 0px 5px 20px 0px rgba(0, 0, 0, 0.15) !important;
background-color: #fff !important;
border: none !important;
width: 25.6rem;
height: 23.9rem;
box-shadow: 0px 11px 20px 0px #0000001a;
border-radius: 0.6rem;
}
// .fida-setting-popover-content {
@@ -349,43 +679,70 @@ const styleOptions = ref<any[]>(
// }
.fida-setting-popover-header {
font-family: 'GeneralMedium';
font-weight: 500;
font-size: 1.6rem;
font-weight: 400;
font-size: 1.4rem;
color: #000;
margin-bottom: 2rem;
margin-bottom: 2rem !important;
}
.fida-setting-popover-content {
padding: 1.6rem 1.4rem 2.2rem !important;
}
.fida-setting-slider-list {
display: flex;
flex-direction: column;
gap: 2rem;
row-gap: 1rem;
}
.fida-setting-slider-item {
.fida-slider-label {
font-family: 'GeneralMedium';
font-weight: 500;
font-size: 1.4rem;
font-weight: 400;
font-size: 1.2rem;
color: #000;
margin-bottom: 0.8rem;
margin-bottom: 1rem;
}
.fida-slider-row {
gap: 1rem;
column-gap: 2.6rem;
.el-slider {
flex: 1;
}
.fida-slider-value {
font-family: 'GeneralMedium';
font-weight: 500;
font-weight: 400;
font-size: 1.4rem;
color: #000;
min-width: 3.5rem;
text-align: right;
}
}
// :deep(.el-slider) {
// }
}
.setting-popover-slider {
--el-slider-height: 0.4rem;
height: fit-content;
.el-slider__runway {
height: var(--el-slider-height);
background-color: #e8e8e8;
border-radius: 0.2rem;
}
.el-slider__bar {
height: var(--el-slider-height);
background-color: #000;
border-radius: 0.2rem;
}
.el-slider__button-wrapper {
width: fit-content;
height: fit-content;
top: 50%;
transform: translate(-50%, -50%);
display: flex;
align-items: center;
}
.el-slider__button {
width: 1rem;
height: 1rem;
background-color: #000;
border-radius: 50%;
border: none;
}
.el-slider__stop {
display: none;
}
}
</style>