<template>
	<page-meta :page-style="officialAccountPopupVisible ? 'overflow: hidden;' : 'overflow: visible;'"></page-meta>
	<view class="release-page">
		<view class="header" :style="{ paddingTop: statusBarTop + 'px' }">
			<view class="back" @tap="back">
				<uni-icons type="left" size="22" color="#18372f"></uni-icons>
			</view>
			<view class="header-title">{{isEdit ? '编辑校园二手' : '发布校园二手'}}</view>
			<view class="header-sub">实名发布，线下面交，请勿提前转账</view>
			<view class="tabs">
				<view class="tab" :class="{ active: publishType == 'sell', disabled: isEdit }" @tap="switchType('sell')">出售</view>
				<view class="tab" :class="{ active: publishType == 'want', disabled: isEdit }" @tap="switchType('want')">求购</view>
			</view>
		</view>

		<view class="form-scroll" :style="{ paddingTop: headerHeight + 'px' }">
			<view class="form-card">
				<view class="field" id="fieldTitle">
					<view class="label">标题</view>
					<input v-model.trim="form.title" maxlength="30" placeholder="最多30个字" />
				</view>

				<view class="field price-row" id="fieldPrice">
					<view class="price-field">
						<view class="label">{{publishType == 'sell' ? '售价' : '预算'}}</view>
						<input v-model="form.price" type="digit" placeholder="0.00" />
					</view>
					<view class="price-field" v-if="publishType == 'sell'">
						<view class="label">原价</view>
						<input v-model="form.originPrice" type="digit" placeholder="可不填" />
					</view>
				</view>

				<view class="field">
					<view class="label">分类</view>
					<picker :range="categoryNames" @change="chooseCategory">
						<view class="picker-value">{{selectedCategoryName || '请选择分类'}} <text>›</text></view>
					</picker>
				</view>

				<view class="field">
					<view class="label">图片 <text>{{publishType == 'sell' ? '最多9张' : '最多1张，可选'}}</text></view>
					<view class="image-list">
						<view class="image-item" v-for="(item,index) in form.images" :key="item">
							<image :src="item" mode="aspectFill"></image>
							<view class="remove" @tap="removeImage(index)">×</view>
						</view>
						<view class="image-add" v-if="form.images.length < maxImageCount" @tap="chooseImages">
							<uni-icons type="plusempty" size="28" color="#8aa09a"></uni-icons>
							<text>上传</text>
						</view>
					</view>
					<view class="audit-inline" :class="{ fail: imageAuditStatus === 'fail' }" v-if="imageAuditing || imageAuditMessage">
						<view class="mini-spinner" v-if="imageAuditing"></view>
						<text>{{imageAuditing ? '图片审核中，请稍候...' : imageAuditMessage}}</text>
					</view>
				</view>

				<view class="field" id="fieldContent">
					<view class="label">{{publishType == 'sell' ? '商品介绍' : '求购内容'}}</view>
					<view class="editor-box">
						<sp-editor
							editor-id="fishReleaseEditor"
							:toolbar-config="editorToolbarConfig"
							:maxlength="1000"
							:placeholder="publishType == 'sell' ? '描述成色、尺寸、交易地点、可取时间等' : '描述想要的物品、预算、交易地点、可取时间等'"
							@init="initEditor"
							@input="inputEditor"
							@upinImage="upinEditorImage"
							@overMax="overEditorMax"
						></sp-editor>
					</view>
				</view>

				<view class="field" id="fieldContact">
					<view class="label">联系方式</view>
					<input v-model.trim="form.wechat" maxlength="20" placeholder="微信号，可选" cursor-spacing="180" />
					<input v-model.trim="form.phone" class="contact-input" maxlength="11" type="number" placeholder="手机号，可选" cursor-spacing="180" />
				</view>

				<view class="field" v-if="publishType == 'sell'">
					<view class="label">商品标签</view>
					<view class="tag-list">
						<view class="tag" :class="{ checked: form.tags.indexOf(item) > -1 }" v-for="item in tagOptions" :key="item" @tap="toggleTag(item)">
							{{item}}
						</view>
					</view>
				</view>
			</view>

			<view class="safe-tip">发布内容、图片、评论都会进行内容安全审核。可以留电话或微信，但请线下面交，谨防诈骗。</view>
			<view class="bottom-space"></view>
		</view>

		<view class="submit-bar">
			<view class="submit-btn" :class="{ disabled: submitting }" @tap="submit">{{submitting ? '提交中...' : '提交发布'}}</view>
		</view>
		<view class="audit-mask" v-if="submitting">
			<view class="audit-card">
				<view class="audit-spinner">
					<view></view>
					<view></view>
					<view></view>
				</view>
				<view class="audit-title">{{auditTitle}}</view>
				<view class="audit-desc">发布内容正在提交审核，请勿重复点击</view>
			</view>
		</view>
		<view class="official-account-mask" v-if="officialAccountPopupVisible" @tap="closeOfficialAccountPopup">
			<view class="official-account-card" @tap.stop>
				<view class="official-account-close" @tap="closeOfficialAccountPopup">×</view>
				<view class="official-account-title">关注公众号</view>
				<view class="official-account-desc">关注后，有新的咨询会及时通知你</view>
				<image
					show-menu-by-longpress
					class="official-account-qr"
					:src="officialAccountQr"
					mode="aspectFill"
					@tap="previewOfficialAccountQr"
				></image>
				<view class="official-account-tip">长按二维码识别关注</view>
				<view class="official-account-btn" @tap="closeOfficialAccountPopup">知道了</view>
			</view>
		</view>
		<ie-auth-dialog />
	</view>
</template>

<script>
	import { getIeProfile } from '@/common/ieApi.js'
	import { ensureIeVerifiedBeforeAction } from '@/common/ieRealNameAuth.js'
	import IeAuthDialog from '@/package1/components/ie-auth-dialog/ie-auth-dialog.vue'
	import SpEditor from '@/package1/components/sp-editor/components/sp-editor/sp-editor.vue'
	import {
		listFishCategories,
		getFishGoodsDetail,
		getFishWantDetail,
		publishFishGoods,
		publishFishWant,
		uploadFishReleaseImage
	} from '@/common/fishMarketApi.js'

	export default {
		components: {
			IeAuthDialog,
			SpEditor
		},
		data() {
			return {
				statusBarTop: 24,
				publishType: 'sell',
				editId: '',
				isEdit: false,
				pendingEditDetail: null,
				pendingEditorHtml: '',
				categories: [],
				selectedCategoryIndex: -1,
				submitting: false,
				submitLocked: false,
				keyboardOpen: false,
				imageChoosing: false,
				imageAuditing: false,
				imageAuditStatus: '',
				imageAuditMessage: '',
				auditTitle: '提交中，等待审核',
				officialAccountPopupVisible: false,
				officialAccountQr: 'https://jewel-shop.oss-cn-beijing.aliyuncs.com/d23a9043a8c24731831ec4db909fe3b4.jpg',
				officialAccountResolve: null,
				editorIns: null,
				editorToolbarConfig: {
					keys: ['bold', 'italic', 'underline', 'header', 'listOrdered', 'listBullet', 'align', 'image', 'removeFormat', 'clear'],
					iconSize: '17px',
					iconColumns: 10
				},
				tagOptions: ['可小刀', '当天可取', '宿舍自提', '可送宿舍', '急出', '九成新'],
				form: {
					title: '',
					price: '',
					originPrice: '',
					content: '',
					contentText: '',
					images: [],
					wechat: '',
					phone: '',
					tags: ['可小刀']
				}
			}
		},
		computed: {
			categoryNames() {
				return this.categories.map(item => item.name)
			},
			selectedCategoryName() {
				const item = this.categories[this.selectedCategoryIndex]
				return item ? item.name : ''
			},
			maxImageCount() {
				return this.publishType == 'sell' ? 9 : 1
			},
			headerHeight() {
				return this.statusBarTop + uni.upx2px(300)
			}
		},
		onLoad(options) {
			if (options && (options.type == 'want' || options.type == 'sell')) {
				this.publishType = options.type
			}
			if (options && options.mode == 'edit' && options.id) {
				this.isEdit = true
				this.editId = options.id
			}
			if (uni.getMenuButtonBoundingClientRect) {
				this.statusBarTop = uni.getMenuButtonBoundingClientRect().top || this.statusBarTop
			}
			this.loadCategories()
			if (this.isEdit) {
				this.loadEditDetail()
			}
			this.ensurePublishVerified()
			this.bindKeyboardListener()
		},
		onUnload() {
			if (uni.offKeyboardHeightChange) {
				uni.offKeyboardHeightChange()
			}
		},
		methods: {
			bindKeyboardListener() {
				if (!uni.onKeyboardHeightChange) return
				uni.onKeyboardHeightChange((res) => {
					this.keyboardOpen = Number(res && res.height) > 0
				})
			},
			back() {
				uni.navigateBack({ fail: () => uni.switchTab({ url: '/pages/index/index' }) })
			},
			switchType(type) {
				if (this.isEdit) return
				this.publishType = type
				if (type == 'want' && this.form.images.length > 1) {
					this.form.images = this.form.images.slice(0, 1)
				}
			},
			getArea() {
				const area = uni.getStorageSync('area')
				if (!area) return {}
				if (typeof area === 'string') {
					try {
						return JSON.parse(area)
					} catch (e) {
						return {}
					}
				}
				return area
			},
			loadCategories() {
				const area = this.getArea()
				listFishCategories(area.id || '').then(list => {
					this.categories = Array.isArray(list) ? list : []
					this.applyEditCategory()
				})
			},
			loadEditDetail() {
				const request = this.publishType == 'want' ? getFishWantDetail(this.editId) : getFishGoodsDetail(this.editId)
				request.then(detail => {
					if (!detail) return
					this.pendingEditDetail = detail
					this.applyEditDetail(detail)
				})
			},
			applyEditDetail(detail) {
				const contact = this.parseJsonObject(detail.contactInfo)
				const imageList = detail.imageList && detail.imageList.length ? detail.imageList : this.parseJsonList(detail.images)
				const tagList = this.publishType == 'sell' ? this.parseJsonList(detail.tags) : []
				const content = detail.content || ''
				this.form.title = detail.title || ''
				this.form.price = String(this.publishType == 'want' ? (detail.expectedPrice || '') : (detail.price || ''))
				this.form.originPrice = this.publishType == 'sell' && detail.originPrice ? String(detail.originPrice) : ''
				this.form.content = content
				this.form.contentText = this.extractText(content)
				this.form.images = imageList.slice(0, this.maxImageCount)
				this.form.wechat = contact.wechat || ''
				this.form.phone = contact.phone || ''
				this.form.tags = tagList.length ? tagList : (this.publishType == 'sell' ? ['可小刀'] : [])
				this.pendingEditorHtml = content
				this.applyEditCategory()
				this.applyPendingEditorContent()
			},
			applyEditCategory() {
				const detail = this.pendingEditDetail
				if (!detail || !this.categories.length) return
				const index = this.categories.findIndex(item => String(item.id) == String(detail.categoryId) || item.name == detail.categoryName)
				this.selectedCategoryIndex = index > -1 ? index : this.selectedCategoryIndex
			},
			applyPendingEditorContent() {
				if (!this.editorIns || !this.pendingEditorHtml) return
				if (this.editorIns.setContents) {
					this.editorIns.setContents({
						html: this.pendingEditorHtml,
						complete: () => this.hideEditKeyboard()
					})
				}
				this.hideEditKeyboard()
			},
			hideEditKeyboard() {
				if (!this.isEdit) return
				setTimeout(() => {
					uni.hideKeyboard()
				}, 120)
			},
			parseJsonList(value) {
				if (!value) return []
				if (Array.isArray(value)) return value
				try {
					const parsed = JSON.parse(value)
					return Array.isArray(parsed) ? parsed : []
				} catch (e) {
					return []
				}
			},
			parseJsonObject(value) {
				if (!value) return {}
				if (typeof value === 'object') return value
				try {
					return JSON.parse(value) || {}
				} catch (e) {
					return {}
				}
			},
			extractText(html) {
				return String(html || '')
					.replace(/<img[^>]*>/gi, '')
					.replace(/<[^>]+>/g, '')
					.replace(/&nbsp;/g, ' ')
					.replace(/&lt;/g, '<')
					.replace(/&gt;/g, '>')
					.replace(/&amp;/g, '&')
					.trim()
			},
			async ensurePublishVerified() {
				const profile = await getIeProfile().catch(() => null)
				const verified = await ensureIeVerifiedBeforeAction({
					profile: profile || {},
					actionText: '发布校园二手信息',
					authDialog: this.fishAuthDialogOptions(),
					reload: getIeProfile
				})
				if (!verified) {
					setTimeout(() => this.back(), 300)
				}
			},
			chooseCategory(e) {
				this.selectedCategoryIndex = Number(e.detail.value)
			},
			chooseImages() {
				if (this.imageChoosing || this.imageAuditing) return
				const remain = this.maxImageCount - this.form.images.length
				if (remain <= 0) return
				this.imageChoosing = true
				this.imageAuditStatus = ''
				this.imageAuditMessage = ''
				uni.chooseImage({
					count: remain,
					sizeType: ['compressed'],
					sourceType: ['album', 'camera'],
					success: async (res) => {
						const paths = res.tempFilePaths || []
						if (!paths.length) return
						this.imageAuditing = true
						uni.showLoading({ title: '图片审核中...', mask: true })
						const failMessages = []
						try {
							for (let i = 0; i < paths.length; i++) {
								const result = await uploadFishReleaseImage(paths[i])
								if (result && result.passed && result.url) {
									this.form.images.push(result.url)
								} else {
									failMessages.push((result && result.message) || '图片审核未通过，请更换图片')
								}
							}
						} finally {
							this.imageAuditing = false
							uni.hideLoading()
						}
						this.showUploadAuditFail(failMessages)
						if (failMessages.length) {
							this.imageAuditStatus = 'fail'
							this.imageAuditMessage = Array.from(new Set(failMessages.filter(Boolean))).join('\n')
						} else {
							this.imageAuditStatus = ''
							this.imageAuditMessage = ''
						}
					},
					fail: (err) => {
						if (err && err.errMsg && err.errMsg.indexOf('cancel') === -1) {
							uni.showToast({ title: '无法选择图片，请检查相册权限', icon: 'none' })
						}
					},
					complete: () => {
						this.imageChoosing = false
					}
				})
			},
			removeImage(index) {
				this.form.images.splice(index, 1)
			},
			initEditor(editor) {
				this.editorIns = editor
				this.applyPendingEditorContent()
			},
			inputEditor(e) {
				this.form.content = e && e.html ? e.html : ''
				this.form.contentText = e && e.text ? e.text : ''
			},
			overEditorMax() {
				uni.showToast({ title: '内容最多1000字', icon: 'none' })
			},
			async upinEditorImage(tempFiles, editorCtx) {
				const files = Array.isArray(tempFiles) ? tempFiles : []
				if (!files.length || !editorCtx) return
				uni.showLoading({ title: '上传中...', mask: true })
				const failMessages = []
				try {
					for (let i = 0; i < files.length; i++) {
						const filePath = files[i].tempFilePath || files[i].path
						const result = filePath ? await uploadFishReleaseImage(filePath) : null
						if (result && result.passed && result.url) {
							editorCtx.insertImage({
								src: result.url,
								width: '90%'
							})
						} else {
							failMessages.push((result && result.message) || '图片审核未通过，请更换图片')
						}
					}
				} finally {
					uni.hideLoading()
				}
				this.showUploadAuditFail(failMessages)
			},
			showUploadAuditFail(messages) {
				if (!messages || !messages.length) return
				const uniq = Array.from(new Set(messages.filter(Boolean)))
				uni.showModal({
					title: '图片审核未通过',
					content: uniq.join('\n'),
					showCancel: false,
					confirmText: '知道了'
				})
			},
			toggleTag(tag) {
				const index = this.form.tags.indexOf(tag)
				if (index > -1) {
					this.form.tags.splice(index, 1)
				} else {
					this.form.tags.push(tag)
				}
			},
			syncEditorContent() {
				return new Promise((resolve) => {
					if (!this.editorIns || !this.editorIns.getContents) {
						resolve()
						return
					}
					this.editorIns.getContents({
						success: (res) => {
							this.form.content = res.html || this.form.content || ''
							this.form.contentText = res.text || this.form.contentText || ''
							resolve()
						},
						fail: () => resolve()
					})
				})
			},
			validate() {
				const title = String(this.form.title || '').trim()
				const price = String(this.form.price || '').trim()
				const originPrice = String(this.form.originPrice || '').trim()
				const contentText = String(this.form.contentText || '').replace(/[ \t\r\n]/g, '')
				const wechat = String(this.form.wechat || '').trim()
				const phone = String(this.form.phone || '').trim()
				if (!title) return '请填写标题'
				if (title.length < 2) return '标题至少2个字'
				if (title.length > 30) return '标题最多30个字'
				if (this.selectedCategoryIndex < 0) return '请选择分类'
				if (!price) return this.publishType == 'sell' ? '请填写售价' : '请填写预算'
				if (!this.isValidPrice(price)) return this.publishType == 'sell' ? '售价必须大于0且最多2位小数' : '预算必须大于0且最多2位小数'
				if (originPrice && !this.isValidAmount(originPrice)) return '原价最多2位小数'
				if (this.publishType == 'sell' && originPrice && Number(price) > Number(originPrice)) return '售价不得大于原价'
				if (this.imageAuditing) return '图片正在审核中，请稍候'
				if (this.publishType == 'sell' && this.form.images.length == 0) return '请至少上传1张商品图片'
				if (this.publishType == 'want' && this.form.images.length > 1) return '求购最多上传1张图片'
				if (!contentText) return this.publishType == 'sell' ? '请填写商品介绍' : '请填写求购内容'
				if (contentText.length < 5) return this.publishType == 'sell' ? '商品介绍至少5个字' : '求购内容至少5个字'
				if (contentText.length > 1000) return this.publishType == 'sell' ? '商品介绍最多1000字' : '求购内容最多1000字'
				if (wechat && !this.isValidWechat(wechat)) return '请输入正确的微信号'
				if (phone && !this.isValidPhone(phone)) return '请输入正确的手机号'
				return ''
			},
			isValidPrice(value) {
				return /^\d+(\.\d{1,2})?$/.test(value) && Number(value) > 0
			},
			isValidAmount(value) {
				return /^\d+(\.\d{1,2})?$/.test(value) && Number(value) >= 0
			},
			isValidWechat(value) {
				return /^[a-zA-Z][-_a-zA-Z0-9]{5,19}$/.test(value)
			},
			isValidPhone(value) {
				return /^1[3-9]\d{9}$/.test(value)
			},
			async submit() {
				if (this.submitLocked) return
				this.submitLocked = true
				let submitSuccess = false
				try {
					await this.syncEditorContent()
					const error = this.validate()
					if (error) {
						uni.showToast({ title: error, icon: 'none' })
						return
					}
					const profile = await getIeProfile().catch(() => null)
					const verified = await ensureIeVerifiedBeforeAction({
						profile: profile || {},
						actionText: '发布校园二手信息',
						authDialog: this.fishAuthDialogOptions(),
						reload: getIeProfile
					})
					if (!verified) return
					const area = this.getArea()
					const category = this.categories[this.selectedCategoryIndex]
					const payload = {
						id: this.isEdit ? this.editId : undefined,
						publishType: this.publishType,
						regionId: area.id || '',
						regionName: area.title || '',
						...this.getCachedUserProfile(),
						title: this.form.title,
						content: this.form.content,
						contentText: this.form.contentText,
						price: this.publishType == 'sell' ? Number(this.form.price) : undefined,
						originPrice: this.form.originPrice ? Number(this.form.originPrice) : undefined,
						expectedPrice: this.publishType == 'want' ? Number(this.form.price) : undefined,
						categoryId: category.id,
						categoryName: category.name,
						images: this.publishType == 'want' ? this.form.images.slice(0, 1) : this.form.images,
						contactInfo: {
							wechat: this.form.wechat,
							phone: this.form.phone
						},
						tags: this.form.tags,
						negotiable: this.form.tags.indexOf('可小刀') > -1 ? 1 : 0
					}
					this.submitting = true
					this.auditTitle = this.isEdit ? '保存中，等待审核' : '提交中，等待审核'
					const res = this.publishType == 'sell' ? await publishFishGoods(payload) : await publishFishWant(payload)
					if (res) {
						submitSuccess = true
						this.auditTitle = this.isEdit ? '保存成功，等待审核' : '提交成功，等待审核'
						this.submitting = false
						await this.handlePublishOfficialAccount(res)
						setTimeout(() => uni.navigateBack(), 300)
					}
				} finally {
					if (!submitSuccess) {
						this.submitting = false
						this.submitLocked = false
					}
				}
			},
			handlePublishOfficialAccount(result) {
				const user = result && result.user ? result.user : null
				if (user) {
					uni.setStorageSync('officialAccountOpenid', user.officialAccountOpenid || '')
				}
				const officialAccountOpenid = user ? user.officialAccountOpenid : uni.getStorageSync('officialAccountOpenid')
				if (officialAccountOpenid) {
					return Promise.resolve()
				}
				return this.openOfficialAccountPopup()
			},
			openOfficialAccountPopup() {
				return new Promise((resolve) => {
					this.officialAccountResolve = resolve
					this.officialAccountPopupVisible = true
				})
			},
			closeOfficialAccountPopup() {
				this.officialAccountPopupVisible = false
				if (this.officialAccountResolve) {
					this.officialAccountResolve()
					this.officialAccountResolve = null
				}
			},
			previewOfficialAccountQr() {
				uni.previewImage({
					current: this.officialAccountQr,
					urls: [this.officialAccountQr]
				})
			},
			fishAuthDialogOptions() {
				return {
					badge: '校园二手',
					title: '实名认证',
					content: '根据法律法规要求，对应流程需要调用阿里云实名认证。半径里不会存储您的实名信息。',
					steps: []
				}
			},
			getCachedUserProfile() {
				return {
					userName: uni.getStorageSync('nickName') || uni.getStorageSync('nickname') || uni.getStorageSync('userName') || '',
					userAvatar: uni.getStorageSync('avatar') || uni.getStorageSync('avatarUrl') || uni.getStorageSync('headimgurl') || ''
				}
			}
		}
	}
</script>

<style lang="scss">
	page {
		background: #f7f9f7;
		color: #15362f;
	}

	.release-page {
		min-height: 100vh;
		overflow: visible;
		background: linear-gradient(180deg, #e7fbf4 0%, #f7f9f7 280rpx);
	}

	.header {
		position: fixed;
		top: 0;
		left: 0;
		right: 0;
		z-index: 20;
		padding: 0 32rpx 28rpx;
		background: linear-gradient(180deg, #e7fbf4 0%, rgba(247, 249, 247, 0.98) 100%);
		box-shadow: 0 8rpx 24rpx rgba(24, 99, 78, 0.05);
		box-sizing: border-box;
	}

	.back {
		width: 64rpx;
		height: 64rpx;
		display: flex;
		align-items: center;
	}

	.header-title {
		margin-top: 12rpx;
		font-size: 42rpx;
		font-weight: 900;
	}

	.header-sub {
		margin-top: 8rpx;
		color: #729088;
		font-size: 24rpx;
	}

	.form-scroll {
		box-sizing: border-box;
	}

	.tabs {
		width: 360rpx;
		height: 72rpx;
		margin: 26rpx 0 0;
		padding: 6rpx;
		display: flex;
		border-radius: 42rpx;
		background: rgba(255, 255, 255, 0.86);
		box-sizing: border-box;
	}

	.tab {
		flex: 1;
		height: 60rpx;
		line-height: 60rpx;
		border-radius: 34rpx;
		text-align: center;
		font-size: 26rpx;
		font-weight: 800;
		color: #7c8d88;
	}

	.tab.active {
		background: linear-gradient(135deg, #39ca8f, #24b979);
		color: #fff;
		box-shadow: 0 8rpx 18rpx rgba(36, 185, 121, 0.22);
	}

	.tab.disabled {
		opacity: .72;
	}

	.form-card {
		margin: 0 24rpx;
		padding: 4rpx 26rpx 26rpx;
		border-radius: 30rpx;
		background: #fff;
		box-shadow: 0 12rpx 32rpx rgba(29, 74, 60, 0.06);
	}

	.field {
		padding-top: 26rpx;
	}

	.label {
		margin-bottom: 14rpx;
		color: #18372f;
		font-size: 27rpx;
		font-weight: 900;
	}

	.label text {
		color: #9aa8a4;
		font-size: 21rpx;
		font-weight: 400;
	}

	input,
	textarea,
	.picker-value {
		width: 100%;
		min-height: 78rpx;
		padding: 0 22rpx;
		border-radius: 18rpx;
		background: #f5f8f6;
		box-sizing: border-box;
		color: #223b35;
		font-size: 26rpx;
	}

	textarea {
		height: 220rpx;
		padding-top: 20rpx;
		line-height: 38rpx;
	}

	.editor-box {
		min-height: 420rpx;
		overflow: hidden;
		border-radius: 18rpx;
		background: #f5f8f6;
		border: 1rpx solid #edf2ef;
		box-sizing: border-box;
	}

	.editor-box ::v-deep .sp-editor-toolbar {
		background: #fff;
		border-bottom: 1rpx solid #edf2ef;
	}

	.editor-box ::v-deep .sp-editor-wrapper {
		min-height: 320rpx;
	}

	.editor-box ::v-deep .editor-container {
		min-height: 320rpx;
		padding: 18rpx 20rpx;
		box-sizing: border-box;
		font-size: 26rpx;
		line-height: 40rpx;
	}

	.picker-value {
		line-height: 78rpx;
	}

	.picker-value text {
		float: right;
		color: #8ca09a;
		font-size: 34rpx;
	}

	.price-row {
		display: flex;
		gap: 18rpx;
	}

	.price-field {
		flex: 1;
	}

	.image-list {
		display: flex;
		flex-wrap: wrap;
		gap: 14rpx;
	}

	.image-item,
	.image-add {
		position: relative;
		width: 150rpx;
		height: 150rpx;
		border-radius: 18rpx;
		overflow: hidden;
		background: #f2f7f4;
	}

	.image-item image {
		width: 100%;
		height: 100%;
	}

	.remove {
		position: absolute;
		top: 8rpx;
		right: 8rpx;
		width: 34rpx;
		height: 34rpx;
		border-radius: 50%;
		background: rgba(0, 0, 0, 0.48);
		color: #fff;
		text-align: center;
		line-height: 30rpx;
	}

	.image-add {
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		color: #8aa09a;
		font-size: 22rpx;
	}

	.audit-inline {
		display: flex;
		align-items: center;
		gap: 10rpx;
		margin-top: 18rpx;
		padding: 16rpx 18rpx;
		border-radius: 18rpx;
		background: #effbf7;
		color: #2c8b72;
		font-size: 23rpx;
		line-height: 1.45;
	}

	.audit-inline.fail {
		background: #fff1ef;
		color: #d64b3c;
	}

	.mini-spinner {
		width: 26rpx;
		height: 26rpx;
		flex: 0 0 26rpx;
		border-radius: 50%;
		border: 4rpx solid #c9f2e8;
		border-top-color: #2fc49f;
		animation: audit-spin .8s linear infinite;
	}

	.contact-input {
		margin-top: 14rpx;
	}

	.tag-list {
		display: flex;
		flex-wrap: wrap;
		gap: 14rpx;
	}

	.tag {
		height: 56rpx;
		padding: 0 20rpx;
		border-radius: 28rpx;
		background: #f2f6f4;
		color: #6f817c;
		font-size: 23rpx;
		line-height: 56rpx;
	}

	.tag.checked {
		background: #e5fbf2;
		color: #1a9a72;
		font-weight: 800;
	}

	.safe-tip {
		margin: 22rpx 32rpx 0;
		color: #84948f;
		font-size: 22rpx;
		line-height: 34rpx;
	}

	.submit-bar {
		position: fixed;
		left: 0;
		right: 0;
		bottom: 0;
		padding: 18rpx 28rpx calc(18rpx + env(safe-area-inset-bottom));
		background: rgba(255, 255, 255, 0.96);
		box-shadow: 0 -8rpx 24rpx rgba(23, 54, 47, 0.06);
	}

	.submit-btn {
		height: 88rpx;
		border-radius: 46rpx;
		background: linear-gradient(135deg, #37c990, #19af7e);
		color: #fff;
		font-size: 30rpx;
		font-weight: 900;
		text-align: center;
		line-height: 88rpx;
	}

	.submit-btn.disabled {
		opacity: 0.62;
	}

	.audit-mask {
		position: fixed;
		left: 0;
		right: 0;
		top: 0;
		bottom: 0;
		z-index: 1200;
		display: flex;
		align-items: center;
		justify-content: center;
		background: rgba(12, 31, 25, 0.34);
	}

	.audit-card {
		width: 480rpx;
		padding: 48rpx 36rpx 42rpx;
		border-radius: 34rpx;
		background: rgba(255, 255, 255, 0.96);
		box-shadow: 0 24rpx 60rpx rgba(19, 70, 54, 0.18);
		text-align: center;
		box-sizing: border-box;
	}

	.audit-spinner {
		width: 124rpx;
		height: 124rpx;
		margin: 0 auto 28rpx;
		display: flex;
		align-items: center;
		justify-content: center;
		gap: 12rpx;
		border-radius: 50%;
		background: linear-gradient(135deg, #e7fbf4, #f8fffc);
	}

	.audit-spinner view {
		width: 16rpx;
		height: 16rpx;
		border-radius: 50%;
		background: #20b985;
		animation: audit-bounce 0.9s infinite ease-in-out;
	}

	.audit-spinner view:nth-child(2) {
		animation-delay: 0.12s;
	}

	.audit-spinner view:nth-child(3) {
		animation-delay: 0.24s;
	}

	.audit-title {
		color: #17362f;
		font-size: 31rpx;
		font-weight: 900;
	}

	.audit-desc {
		margin-top: 14rpx;
		color: #84948f;
		font-size: 23rpx;
		line-height: 34rpx;
	}

	.official-account-mask {
		position: fixed;
		left: 0;
		right: 0;
		top: 0;
		bottom: 0;
		z-index: 1300;
		display: flex;
		align-items: center;
		justify-content: center;
		padding: 40rpx;
		background: rgba(12, 31, 25, 0.46);
		box-sizing: border-box;
	}

	.official-account-card {
		position: relative;
		width: 590rpx;
		padding: 46rpx 42rpx 38rpx;
		border-radius: 36rpx;
		background: #fff;
		text-align: center;
		box-shadow: 0 26rpx 64rpx rgba(19, 70, 54, 0.22);
		box-sizing: border-box;
	}

	.official-account-close {
		position: absolute;
		right: 22rpx;
		top: 18rpx;
		width: 54rpx;
		height: 54rpx;
		line-height: 50rpx;
		border-radius: 50%;
		color: #8b9b96;
		font-size: 44rpx;
	}

	.official-account-title {
		color: #17362f;
		font-size: 34rpx;
		font-weight: 900;
	}

	.official-account-desc {
		margin-top: 12rpx;
		color: #6f817c;
		font-size: 24rpx;
		line-height: 36rpx;
	}

	.official-account-qr {
		width: 330rpx;
		height: 330rpx;
		margin: 30rpx auto 0;
		display: block;
		border-radius: 24rpx;
		background: #f5f8f6;
	}

	.official-account-tip {
		margin-top: 18rpx;
		color: #1a9a72;
		font-size: 24rpx;
		font-weight: 800;
	}

	.official-account-btn {
		height: 78rpx;
		margin-top: 30rpx;
		border-radius: 42rpx;
		background: linear-gradient(135deg, #37c990, #19af7e);
		color: #fff;
		font-size: 28rpx;
		font-weight: 900;
		line-height: 78rpx;
	}

	@keyframes audit-bounce {
		0%,
		80%,
		100% {
			transform: translateY(0);
			opacity: 0.45;
		}

		40% {
			transform: translateY(-18rpx);
			opacity: 1;
		}
	}

	@keyframes audit-spin {
		to {
			transform: rotate(360deg);
		}
	}

	.bottom-space {
		height: 240rpx;
	}
</style>
