构建: - 新增 tools/build.js:junction HBuilderX 工具链,CLI 构建 h5/mp-weixin - vue 指向补丁版 @dcloudio/uni-h5-vue(官方 npm vue 不导出 isInSSRComponentSetup) - 设 HX_APP_ROOT 避免退化成 H5 空壳产物;产物完整性校验 校验工具: - 新增 check-cloud-methods.js:acorn 解析云对象方法,比对 94 处调用点 - 新增 check-android-contract.js:Kotlin 侧云对象契约校验 - audit-project.js 修 downloadFile 误报(注释未剥离);tools/ 排除出扫描 - package.json 声明此前隐式依赖的 acorn 功能: - 补 uni-cms-articles.getPublishedArticles(安卓端依赖但此前不存在) - 修 u-parse <audio> 引用已移除组件导致 H5 构建失败
876 lines
21 KiB
Vue
876 lines
21 KiB
Vue
<template>
|
|
<view class="b_col_eee body">
|
|
<uni-nav-bar
|
|
leftText="取消"
|
|
left-icon="left"
|
|
right-text="下一步"
|
|
backgroundColor="#ffffff"
|
|
color="#000000"
|
|
@clickRight="toEditSetting()"
|
|
@clickLeft="back()">
|
|
</uni-nav-bar>
|
|
|
|
<uni-popup
|
|
ref="alertDialog"
|
|
type="dialog">
|
|
<uni-popup-dialog
|
|
type="info"
|
|
cancelText="放弃草稿"
|
|
confirmText="编辑草稿"
|
|
title="提示"
|
|
content="文章有草稿未完成,是否继续编辑草稿?"
|
|
@confirm="dialogConfirm"
|
|
@close="dialogClose">
|
|
</uni-popup-dialog>
|
|
</uni-popup>
|
|
|
|
<uni-popup ref="del_popup" type='bottom'>
|
|
<view class="del-popup">
|
|
<view class="margin_20_top padding_20 color-666 b_col_fff">
|
|
确认要删除当前文章段落
|
|
</view>
|
|
<view class="margin_4 padding_20 color-red b_col_fff" @click="delItem()">
|
|
<uni-icons type="trash" size="30" color="#f00"></uni-icons>
|
|
删除
|
|
</view>
|
|
<view class="text-center padding_20 margin_10_top b_col_fff" @click="close_del_popup()">
|
|
取消
|
|
</view>
|
|
</view>
|
|
|
|
</uni-popup>
|
|
|
|
<uni-popup ref="popup" type='bottom'>
|
|
<view class="b_col_fff">
|
|
<view class="grid-container">
|
|
<view v-for="(item, index) in cmsType" :key="index" :class="{
|
|
'grid-item': true,
|
|
}" @click="addItem(item.type)">
|
|
<view class="grid-item-box">
|
|
<image :src="item.src" class="image" mode="aspectFill" />
|
|
<text class="text">
|
|
{{ item.text }}
|
|
</text>
|
|
</view>
|
|
</view>
|
|
|
|
</view>
|
|
</view>
|
|
</uni-popup>
|
|
<view
|
|
@click="toTitleStyle()"
|
|
class="margin_50 b_col_fff padding_10"
|
|
style="display: flex; align-items: center; gap: 20rpx;">
|
|
<uni-easyinput
|
|
|
|
:inputBorder="false"
|
|
disabled
|
|
v-model="cms.title"
|
|
placeholder="请输入标题"
|
|
style="flex: 1;"
|
|
></uni-easyinput>
|
|
<view class="border_ff0" style="white-space: nowrap;">
|
|
<uni-icons type="gear" size="20" color="993300"></uni-icons>
|
|
标题样式
|
|
</view>
|
|
</view>
|
|
|
|
<view class="container">
|
|
<button class="add-button" @click="addEditItem(0)"></button>
|
|
</view>
|
|
|
|
<view v-for="(item, index) in cms.cmsLst" :key="index">
|
|
<view class="margin_20 b_col_fff edit_item padding_10">
|
|
|
|
<view class="part part1">
|
|
|
|
<view v-if="item.type === 'image'" @click="updateImage(index)">
|
|
<cloud-image
|
|
mode="scaleToFill"
|
|
width="100%"
|
|
height="200rpx"
|
|
v-if="item.image.src"
|
|
:src="item.image.src">
|
|
</cloud-image>
|
|
<view class="not-image" v-else>
|
|
<cloud-image
|
|
mode="scaleToFill"
|
|
width="100%"
|
|
height="150rpx"
|
|
:src="not_image">
|
|
</cloud-image>
|
|
<view class="not-font">
|
|
添加图片
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="video-container"
|
|
v-if="item.type === 'video'"
|
|
@click="updateVideoItem(index)">
|
|
<cloud-image
|
|
v-if="item.video.poster"
|
|
mode="scaleToFill"
|
|
width="100%"
|
|
height="200rpx"
|
|
:src="item.video.poster">
|
|
</cloud-image>
|
|
<cloud-image
|
|
class="icon-play"
|
|
mode="scaleToFill"
|
|
width="100rpx"
|
|
height="100rpx"
|
|
src="/static/editor-icons/stop.png">
|
|
</cloud-image>
|
|
</view>
|
|
|
|
<view
|
|
v-if="item.type === 'vote'">
|
|
<view class="not-image">
|
|
<view class="not-image-icon">
|
|
<cloud-image
|
|
mode="scaleToFill"
|
|
width="100%"
|
|
height="150rpx"
|
|
:src="is_vote">
|
|
</cloud-image>
|
|
</view>
|
|
<view class="not-font">
|
|
投票
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="part part2" @click="to(item.type, index)">
|
|
<view class="" v-if="item.type === 'vote'">
|
|
<view v-if="item.text">
|
|
{{ item.text }}
|
|
</view>
|
|
<view class="font-info" v-else>
|
|
点击添加文字
|
|
</view>
|
|
</view>
|
|
|
|
<view class="" v-else>
|
|
<view v-if="item.text">
|
|
{{ item.text }}
|
|
</view>
|
|
<view class="font-info" v-else>
|
|
点击添加文字
|
|
</view>
|
|
</view>
|
|
|
|
</view>
|
|
|
|
<view class="part part3">
|
|
<uni-icons
|
|
type="closeempty"
|
|
class="sub-area"
|
|
@click="open_del_popup(index)">
|
|
</uni-icons>
|
|
<uni-icons
|
|
type="bars"
|
|
class="sub-area">
|
|
</uni-icons>
|
|
<view class="sub-area item-no">
|
|
{{ index + 1 }}
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<view class="container">
|
|
<button
|
|
class="add-button"
|
|
@click="addEditItem(index + 1)">
|
|
</button>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import { ref, reactive } from 'vue'
|
|
import { onLoad, onShow, onReady, onHide, onUnload } from '@dcloudio/uni-app'
|
|
import cloudImage from "@/components/cloud-image.vue"
|
|
|
|
const uniCloudStorageExtCo = uniCloud.importObject("ext-storage-co")
|
|
|
|
export default {
|
|
components: {
|
|
cloudImage
|
|
},
|
|
setup() {
|
|
const alertDialog = ref(null)
|
|
const delPopup = ref(null)
|
|
const popup = ref(null)
|
|
const saveInterval = ref(null)
|
|
// 有值表示在编辑一篇已存在的文章(带 id 进入)
|
|
const editingId = ref("")
|
|
|
|
// 图标改为本地静态资源:原先的外链依赖第三方域名与签名,失效即整块空白
|
|
const is_vote = ref("/static/editor-icons/is_vote.png")
|
|
const not_image = ref("/static/editor-icons/not_image.png")
|
|
const selectIndex = ref(0)
|
|
|
|
const cmsType = ref([
|
|
{ type: "text", text: "文字", src: "/static/editor-icons/text.png" },
|
|
{ type: "video", text: "视频", src: "/static/editor-icons/video.png" },
|
|
{ type: "image", text: "图片", src: "/static/editor-icons/image.png" },
|
|
{ type: "vote", text: "投票", src: "/static/editor-icons/vote.png" },
|
|
{ type: "title", text: "小标题", src: "/static/editor-icons/title.png" },
|
|
])
|
|
|
|
const cms = reactive({
|
|
title_html: "",
|
|
title_delta: {},
|
|
title: "",
|
|
cmsLst: []
|
|
})
|
|
|
|
onHide(() => {
|
|
clearInterval(saveInterval.value)
|
|
})
|
|
|
|
onUnload(() => {
|
|
clearInterval(saveInterval.value)
|
|
clearTimeout(saveInterval.value)
|
|
})
|
|
|
|
onLoad((options) => {
|
|
// 从「我的作品」进入时带 id,用于区分"编辑已有文章"与"新建"
|
|
editingId.value = (options && options.id) || ""
|
|
})
|
|
|
|
onReady(() => {
|
|
const storedCms = uni.getStorageSync("cms")
|
|
const hasDraft = storedCms && (
|
|
(storedCms.cmsLst && storedCms.cmsLst.length > 0) ||
|
|
storedCms.title ||
|
|
storedCms.title_html
|
|
)
|
|
// 从「我的作品」带 id 进来是编辑已有文章,不是未完成的草稿,
|
|
// 此时弹"草稿未完成"会误导用户。只有新建流程才提示。
|
|
const editingExisting = !!(editingId && editingId.value)
|
|
if (hasDraft && !editingExisting) {
|
|
alertDialog.value.open()
|
|
}
|
|
saveInterval.value = setInterval(saveCms, 10000)
|
|
})
|
|
|
|
onShow(() => {
|
|
const storedCms = uni.getStorageSync("cms")
|
|
if (storedCms) {
|
|
Object.assign(cms, storedCms)
|
|
}
|
|
})
|
|
|
|
function saveCms() {
|
|
// 定时保存只在有实际内容时落盘,避免空编辑器把上一次的草稿覆盖成空
|
|
const hasContent = (cms.cmsLst && cms.cmsLst.length > 0) || cms.title || cms.title_html
|
|
if (!hasContent) return
|
|
uni.setStorageSync("cms", JSON.parse(JSON.stringify(cms)))
|
|
}
|
|
|
|
function open() {
|
|
popup.value.open()
|
|
}
|
|
|
|
function close() {
|
|
popup.value.close()
|
|
}
|
|
|
|
function confirm(value) {
|
|
popup.value.close()
|
|
}
|
|
|
|
function addEditItem(index) {
|
|
selectIndex.value = index
|
|
uni.setStorageSync("selectIndex", selectIndex.value)
|
|
open()
|
|
}
|
|
|
|
function getFileInfo(path) {
|
|
const parts = path.split('/').pop()
|
|
const [filename, ...extParts] = parts.split('.')
|
|
const ext = extParts.length ? extParts.pop() : ''
|
|
return { name: filename, extname: ext }
|
|
}
|
|
|
|
function formatBytes(bytes) {
|
|
if (bytes === 0) return '0 Bytes'
|
|
const k = 1024
|
|
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']
|
|
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
if (i >= sizes.length) return '> 1 TB'
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
|
|
}
|
|
|
|
async function updateImage(index) {
|
|
uni.chooseImage({
|
|
sourceType: ["album", "camera"],
|
|
count: 1,
|
|
success: async (res) => {
|
|
if (!res.tempFilePaths || !res.tempFilePaths.length) return
|
|
const oldSrc = cms.cmsLst[index]?.image?.src || ''
|
|
try {
|
|
const filePath = res.tempFilePaths[0]
|
|
const { name } = getFileInfo(filePath)
|
|
const options = await uniCloudStorageExtCo.getUploadFileOptions({
|
|
cloudPath: `cms/image/${name}_${Math.random().toString(36).substr(2, 6)}`
|
|
})
|
|
const uploaded = await new Promise((resolve, reject) => uniCloud.uploadFile({
|
|
...options.uploadFileOptions,
|
|
filePath,
|
|
cloudPath: options.cloudPath,
|
|
success: resolve,
|
|
fail: reject
|
|
}))
|
|
cms.cmsLst[index].image = { ...cms.cmsLst[index].image, src: uploaded.fileID }
|
|
uni.setStorageSync('cms', JSON.parse(JSON.stringify(cms)))
|
|
if (oldSrc && editingId.value) {
|
|
try { await uniCloudStorageExtCo.deleteFile({ fileList: [oldSrc], article_id: editingId.value }) } catch (e) { console.warn('旧图片清理失败', e) }
|
|
}
|
|
} catch (err) {
|
|
console.error('图片替换失败', err)
|
|
uni.showToast({ title: '图片上传失败', icon: 'none' })
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
async function delItem() {
|
|
let index = selectIndex.value
|
|
if (index >= 0 && index < cms.cmsLst.length) {
|
|
if (cms.cmsLst[index].type === "image" && editingId.value && cms.cmsLst[index].image?.src) {
|
|
try {
|
|
await uniCloudStorageExtCo.deleteFile({ fileList: [cms.cmsLst[index].image.src], article_id: editingId.value })
|
|
} catch (e) {
|
|
console.warn('旧图片清理失败', e)
|
|
}
|
|
} else if (cms.cmsLst[index].type === "video" && editingId.value && (cms.cmsLst[index].video?.src || cms.cmsLst[index].video?.poster)) {
|
|
try {
|
|
await uniCloudStorageExtCo.deleteFile({
|
|
fileList: [cms.cmsLst[index].video.src, cms.cmsLst[index].video.poster],
|
|
article_id: editingId.value
|
|
})
|
|
} catch (e) {
|
|
console.warn('旧视频清理失败', e)
|
|
}
|
|
}
|
|
cms.cmsLst.splice(index, 1)
|
|
uni.setStorageSync('cms', JSON.parse(JSON.stringify(cms)))
|
|
}
|
|
delPopup.value.close()
|
|
}
|
|
|
|
async function addImageItem() {
|
|
uni.chooseImage({
|
|
sourceType: ["album", "camera"],
|
|
count: 9,
|
|
success: async (res) => {
|
|
if (res.tempFilePaths.length > 0) {
|
|
for (let i = 0; i < res.tempFilePaths.length; i++) {
|
|
let filePath = res.tempFilePaths[i]
|
|
let { name, extname } = getFileInfo(filePath)
|
|
const uploadFileOptionsRes = await uniCloudStorageExtCo.getUploadFileOptions({
|
|
cloudPath: `cms/image/${name}_${Math.random().toString(36).substr(2,6)}`
|
|
})
|
|
await uniCloud.uploadFile({
|
|
...uploadFileOptionsRes.uploadFileOptions,
|
|
filePath: filePath,
|
|
cloudPath: uploadFileOptionsRes.cloudPath,
|
|
success: async () => {
|
|
const res2 = {
|
|
cloudPath: uploadFileOptionsRes.cloudPath,
|
|
fileID: uploadFileOptionsRes.fileID,
|
|
fileURL: uploadFileOptionsRes.fileURL
|
|
}
|
|
const selectedMedia = { src: res2.fileID, alt: name }
|
|
let item = {
|
|
type: "image",
|
|
text: "",
|
|
image: { src: selectedMedia.src, html: "", delta: {} }
|
|
}
|
|
cms.cmsLst.splice(selectIndex.value + i, 0, item)
|
|
close()
|
|
},
|
|
fail: (err) => {
|
|
console.log("上传失败", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
function back() {
|
|
saveCms()
|
|
try {
|
|
uni.navigateBack({ delta: 1 })
|
|
} catch (e) {
|
|
uni.switchTab({ url: "/uni_modules/uni-cms-article/pages/list/list" })
|
|
}
|
|
}
|
|
|
|
function toeditText(index) {
|
|
if (index >= 0) {
|
|
uni.setStorageSync("selectIndex", index)
|
|
}
|
|
saveCms()
|
|
uni.navigateTo({ url: "/pages2/editCms/editText" })
|
|
}
|
|
|
|
function open_del_popup(index) {
|
|
selectIndex.value = index
|
|
delPopup.value.open()
|
|
}
|
|
|
|
function close_del_popup() {
|
|
delPopup.value.close()
|
|
}
|
|
|
|
function toTitleStyle() {
|
|
saveCms()
|
|
uni.navigateTo({ url: "/pages2/editCms/titleStyle" })
|
|
}
|
|
|
|
function toVote(index) {
|
|
if (index >= 0) {
|
|
uni.setStorageSync("selectIndex", index)
|
|
}
|
|
saveCms()
|
|
uni.navigateTo({ url: "/pages2/editCms/vote?type=edit" })
|
|
}
|
|
|
|
function to(type, index) {
|
|
if (type === 'vote') {
|
|
toVote(index)
|
|
} else if (type === 'title') {
|
|
uni.setStorageSync('selectIndex', index)
|
|
saveCms()
|
|
uni.navigateTo({ url: '/pages2/editCms/smallTitle' })
|
|
} else {
|
|
toeditText(index)
|
|
}
|
|
}
|
|
|
|
function toEditSetting() {
|
|
saveCms()
|
|
uni.navigateTo({ url: "/pages2/editCms/editSetting" })
|
|
}
|
|
|
|
function addTextItem() {
|
|
cms.cmsLst.splice(selectIndex.value, 0, {
|
|
type: "text",
|
|
text: "",
|
|
image: { src: "", html: "", delta: {} },
|
|
html: "",
|
|
delta: {}
|
|
})
|
|
toeditText()
|
|
}
|
|
|
|
function addVoteItem() {
|
|
saveCms()
|
|
uni.navigateTo({ url: "/pages2/editCms/vote?type=add" })
|
|
}
|
|
|
|
function addTitleItem() {
|
|
saveCms()
|
|
uni.navigateTo({ url: "/pages2/editCms/smallTitle" })
|
|
}
|
|
|
|
// 放弃草稿只清理本地状态;媒体删除由服务端按文章引用关系处理
|
|
async function discardDraft() {
|
|
uni.setStorageSync("cms", { cmsLst: [], title: "", title_html: "", title_delta: {} })
|
|
Object.assign(cms, uni.getStorageSync("cms"))
|
|
alertDialog.value?.close()
|
|
}
|
|
|
|
async function dialogClose() {
|
|
await discardDraft()
|
|
}
|
|
|
|
// 「编辑草稿」:保留本地草稿(onShow 已把内容灌进 cms),直接进入编辑
|
|
async function dialogConfirm() {
|
|
alertDialog.value?.close()
|
|
}
|
|
|
|
async function addItem(type) {
|
|
if (type === "text") {
|
|
addTextItem()
|
|
} else if (type === "image") {
|
|
await addImageItem()
|
|
} else if (type === "video") {
|
|
await addVideoItem()
|
|
} else if (type === "title") {
|
|
addTitleItem()
|
|
} else if (type === "vote") {
|
|
addVoteItem()
|
|
}
|
|
}
|
|
|
|
async function addVideoItem() {
|
|
uni.chooseVideo({
|
|
sourceType: ['album', 'camera'],
|
|
success: async (res) => {
|
|
let filePath = res.tempFilePath
|
|
let { name, extname } = getFileInfo(filePath)
|
|
let duration = res.duration
|
|
const uploadFileOptionsRes = await uniCloudStorageExtCo.getUploadFileOptions({
|
|
cloudPath: `cms/video/${name}_${Math.random().toString(36).substr(2,6)}`
|
|
})
|
|
await uniCloud.uploadFile({
|
|
...uploadFileOptionsRes.uploadFileOptions,
|
|
filePath: filePath,
|
|
cloudPath: uploadFileOptionsRes.cloudPath,
|
|
success: async () => {
|
|
const res2 = {
|
|
cloudPath: uploadFileOptionsRes.cloudPath,
|
|
fileID: uploadFileOptionsRes.fileID,
|
|
fileURL: uploadFileOptionsRes.fileURL
|
|
}
|
|
const selectedMedia = { src: res2.fileURL, alt: name }
|
|
let poster = "vframe/jpg/offset/0/w/440/h/264/"
|
|
let cover = res2.fileURL + "?" + poster
|
|
let item = {
|
|
type: "video",
|
|
text: "",
|
|
video: { src: selectedMedia.src, poster: cover, duration: duration, html: "", delta: {} }
|
|
}
|
|
cms.cmsLst.splice(selectIndex.value, 0, item)
|
|
close()
|
|
},
|
|
fail: (err) => {
|
|
console.log("上传失败", err)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
async function updateVideoItem(index) {
|
|
uni.chooseVideo({
|
|
sourceType: ['album', 'camera'],
|
|
success: async (res) => {
|
|
const oldVideo = { ...(cms.cmsLst[index]?.video || {}) }
|
|
let filePath = res.tempFilePath
|
|
let { name, extname } = getFileInfo(filePath)
|
|
let duration = res.duration
|
|
const uploadFileOptionsRes = await uniCloudStorageExtCo.getUploadFileOptions({
|
|
cloudPath: `cms/video/${name}_${Math.random().toString(36).substr(2,6)}`
|
|
})
|
|
await uniCloud.uploadFile({
|
|
...uploadFileOptionsRes.uploadFileOptions,
|
|
filePath: filePath,
|
|
cloudPath: uploadFileOptionsRes.cloudPath,
|
|
success: async () => {
|
|
const res2 = {
|
|
cloudPath: uploadFileOptionsRes.cloudPath,
|
|
fileID: uploadFileOptionsRes.fileID,
|
|
fileURL: uploadFileOptionsRes.fileURL
|
|
}
|
|
const selectedMedia = { src: res2.fileURL, alt: name }
|
|
let poster = "vframe/jpg/offset/0/w/440/h/264/"
|
|
let cover = res2.fileURL + "?" + poster
|
|
let { text, html, delta } = {
|
|
text: cms.cmsLst[index].text,
|
|
html: cms.cmsLst[index].video.html,
|
|
delta: cms.cmsLst[index].video.delta
|
|
}
|
|
let item = {
|
|
type: "video",
|
|
text: text,
|
|
video: { src: selectedMedia.src, poster: cover, duration: duration, html: html, delta: delta }
|
|
}
|
|
cms.cmsLst[index] = item
|
|
uni.setStorageSync('cms', JSON.parse(JSON.stringify(cms)))
|
|
if (editingId.value && (oldVideo.src || oldVideo.poster)) {
|
|
try {
|
|
await uniCloudStorageExtCo.deleteFile({
|
|
fileList: [oldVideo.src, oldVideo.poster],
|
|
article_id: editingId.value
|
|
})
|
|
} catch (e) { console.warn('旧视频清理失败', e) }
|
|
}
|
|
close()
|
|
},
|
|
fail: (err) => {
|
|
console.log("上传失败", err)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
return {
|
|
alertDialog,
|
|
delPopup,
|
|
popup,
|
|
saveInterval,
|
|
editingId,
|
|
is_vote,
|
|
not_image,
|
|
selectIndex,
|
|
cmsType,
|
|
cms,
|
|
saveCms,
|
|
open,
|
|
close,
|
|
confirm,
|
|
addEditItem,
|
|
getFileInfo,
|
|
formatBytes,
|
|
updateImage,
|
|
delItem,
|
|
addImageItem,
|
|
back,
|
|
toeditText,
|
|
open_del_popup,
|
|
close_del_popup,
|
|
toTitleStyle,
|
|
toVote,
|
|
to,
|
|
toEditSetting,
|
|
addTextItem,
|
|
addVoteItem,
|
|
addTitleItem,
|
|
dialogClose,
|
|
dialogConfirm,
|
|
addItem,
|
|
addVideoItem,
|
|
updateVideoItem
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.b_col_fff {
|
|
background-color: #fff;
|
|
}
|
|
|
|
.b_col_eee {
|
|
background-color: #eee;
|
|
}
|
|
|
|
.body {
|
|
width: 100vw;
|
|
min-height: 100vh;
|
|
}
|
|
|
|
.margin_20 {
|
|
margin-top: 20rpx;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
.margin_10_top {
|
|
margin-top: 10rpx;
|
|
}
|
|
.margin_10 {
|
|
margin-top: 10rpx;
|
|
margin-bottom: 10rpx;
|
|
}
|
|
|
|
.container {
|
|
position: relative;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
|
|
.add-button {
|
|
width: 60rpx;
|
|
height: 60rpx;
|
|
border-radius: 50%;
|
|
border: 2rpx solid #ccc;
|
|
background-color: #eee;
|
|
cursor: pointer;
|
|
position: relative;
|
|
transition: transform 0.3s ease;
|
|
}
|
|
|
|
.add-button::before,
|
|
.add-button::after {
|
|
content: '';
|
|
position: absolute;
|
|
background-color: #ccc;
|
|
left: 50%;
|
|
top: 50%;
|
|
transform: translate(-50%, -50%);
|
|
}
|
|
|
|
.add-button::before {
|
|
width: 24rpx;
|
|
height: 4rpx;
|
|
}
|
|
|
|
.color-red {
|
|
color: #f00;
|
|
}
|
|
|
|
.add-button::after {
|
|
width: 4rpx;
|
|
height: 24rpx;
|
|
}
|
|
|
|
.add-button:active {
|
|
transform: scale(0.95);
|
|
}
|
|
|
|
.edit_item {
|
|
width: 100vw;
|
|
height: 200rpx;
|
|
display: flex;
|
|
|
|
.part {
|
|
height: 100%;
|
|
|
|
|
|
&.part1 {
|
|
flex: 0 0 30%;
|
|
}
|
|
|
|
&.part2 {
|
|
flex: 0 0 63%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
&.part3 {
|
|
flex: 0 0 7%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-direction: column;
|
|
|
|
.sub-area {
|
|
flex: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.item-no {
|
|
background-color: #ccc;
|
|
color: #fff;
|
|
text-align: center;
|
|
width: 1.5rem;
|
|
height: 1.5rem;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.margin_20_top {
|
|
margin-top: 20rpx;
|
|
}
|
|
|
|
.padding_10 {
|
|
padding: 20rpx;
|
|
}
|
|
|
|
.padding_20 {
|
|
padding: 20rpx;
|
|
}
|
|
|
|
.font-info {
|
|
color: #666;
|
|
}
|
|
|
|
.grid-container {
|
|
.grid-item {
|
|
width: calc(98% / 3);
|
|
}
|
|
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
box-sizing: border-box;
|
|
padding: 10rpx;
|
|
width: 100%;
|
|
}
|
|
|
|
.grid-item-box {
|
|
flex: 1;
|
|
/* #ifndef APP-NVUE */
|
|
display: flex;
|
|
/* #endif */
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 15px 0;
|
|
}
|
|
|
|
.margin_4 {
|
|
margin-top: 4rpx;
|
|
margin-bottom: 4rpx;
|
|
}
|
|
|
|
.image {
|
|
width: 50rpx;
|
|
height: 50rpx;
|
|
}
|
|
.not-image {
|
|
background-color: #eee;
|
|
width: 100%;
|
|
height: 200rpx;
|
|
text-align: center;
|
|
color: #fff;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
align-items: center;
|
|
.not-image-icon {
|
|
justify-content: center;
|
|
align-items: center;
|
|
text-align: center;
|
|
}
|
|
}
|
|
.not-font {
|
|
background-color: #7a7e83;
|
|
}
|
|
.margin_50 {
|
|
margin-top: 50rpx;
|
|
margin-bottom: 30rpx;
|
|
}
|
|
.del-popup {
|
|
background-color: #ccc;
|
|
border-radius: 20rpx 0rpx;
|
|
font-size: 40rpx;
|
|
}
|
|
|
|
.text-center {
|
|
text-align: center;
|
|
}
|
|
|
|
.margin_30_left {
|
|
margin-left: 30rpx;
|
|
}
|
|
|
|
.color-eee {
|
|
color: #eee;
|
|
}
|
|
|
|
.color-666 {
|
|
color: #666;
|
|
}
|
|
.border_ff0 {
|
|
border: 2rpx solid #993300;
|
|
background-color: #ff0;
|
|
border-radius: 10rpx;
|
|
padding: 10rpx;
|
|
}
|
|
|
|
.icon-play {
|
|
position: absolute;
|
|
left: 50%;
|
|
top: 50%;
|
|
transform: translate(-50%, -50%);
|
|
font-size: 50rpx;
|
|
color: rgba(255, 255, 255, 0.8);
|
|
}
|
|
.video-container {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 200rpx;
|
|
}
|
|
</style>
|