修复h5
This commit is contained in:
@@ -18,6 +18,7 @@
|
|||||||
@import 'uview-plus/index.scss';
|
@import 'uview-plus/index.scss';
|
||||||
@import './src/styles/theme.scss';
|
@import './src/styles/theme.scss';
|
||||||
@import './src/styles/fontawesome.scss';
|
@import './src/styles/fontawesome.scss';
|
||||||
|
@import './src/styles/rich-content.scss';
|
||||||
|
|
||||||
page {
|
page {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
+25
-7
@@ -1,12 +1,13 @@
|
|||||||
/**
|
/**
|
||||||
* 从项目根目录 .env 读取接口地址。
|
* 接口 baseURL 解析。
|
||||||
* 变量名:VITE_API_BASE_URL
|
* 优先级:VITE_API_BASE_URL > H5 开发代理 > H5 生产域名推断 > 默认生产 API
|
||||||
* 调试默认 localhost;正式构建前在 .env 里改成 https://api.yunzer.cn 即可。
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** 极验 4.0 captcha_id(前端展示用;KEY 仅在后端校验) */
|
/** 极验 4.0 captcha_id(前端展示用;KEY 仅在后端校验) */
|
||||||
export const GEETEST4_CAPTCHA_ID = '75e8a175c43b9ecfa15372c658be05e5'
|
export const GEETEST4_CAPTCHA_ID = '75e8a175c43b9ecfa15372c658be05e5'
|
||||||
|
|
||||||
|
const DEFAULT_PROD_API = 'https://api.yunzer.cn'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {string} 去掉末尾斜杠的 baseURL
|
* @returns {string} 去掉末尾斜杠的 baseURL
|
||||||
*/
|
*/
|
||||||
@@ -27,14 +28,31 @@ export function resolveBaseURL() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// #ifdef H5
|
// #ifdef H5
|
||||||
// H5 开发未配置 env 时走 vite 代理(前缀勿用 /api,会与 api/ 源码目录冲突)
|
if (typeof window !== 'undefined' && window.location) {
|
||||||
if (typeof import.meta !== 'undefined' && import.meta.env && import.meta.env.DEV) {
|
const { hostname, protocol } = window.location
|
||||||
|
|
||||||
|
// 生产域名优先:避免 HBuilderX 构建后 DEV 仍为 true 时误走 /proxy-api
|
||||||
|
if (hostname.includes('yunzer.cn')) {
|
||||||
|
return DEFAULT_PROD_API
|
||||||
|
}
|
||||||
|
|
||||||
|
// 本地 H5 开发走 vite 代理
|
||||||
|
if (
|
||||||
|
(hostname === 'localhost' || hostname === '127.0.0.1') &&
|
||||||
|
typeof import.meta !== 'undefined' &&
|
||||||
|
import.meta.env &&
|
||||||
|
import.meta.env.DEV
|
||||||
|
) {
|
||||||
return '/proxy-api'
|
return '/proxy-api'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hostname === 'localhost' || hostname === '127.0.0.1') {
|
||||||
|
return `${protocol}//${hostname}:9000`.replace(/\/$/, '')
|
||||||
|
}
|
||||||
|
}
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
// App / 真机:localhost 指向设备自身,需用 .env.development 配置局域网 IP
|
return DEFAULT_PROD_API
|
||||||
return 'http://localhost:9000'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 与 resolveBaseURL 相同,便于业务侧统一引用 */
|
/** 与 resolveBaseURL 相同,便于业务侧统一引用 */
|
||||||
|
|||||||
@@ -5,10 +5,12 @@
|
|||||||
import { getToken, logout } from '@/utils/auth.js'
|
import { getToken, logout } from '@/utils/auth.js'
|
||||||
import { resolveBaseURL, getBaseURL } from '@/api/config.js'
|
import { resolveBaseURL, getBaseURL } from '@/api/config.js'
|
||||||
|
|
||||||
const BASE_URL = resolveBaseURL()
|
|
||||||
|
|
||||||
export { getBaseURL }
|
export { getBaseURL }
|
||||||
|
|
||||||
|
function buildFullUrl(url) {
|
||||||
|
return resolveBaseURL().replace(/\/$/, '') + url
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {{ url: string, method?: string, data?: object, header?: object, silent?: boolean }} options
|
* @param {{ url: string, method?: string, data?: object, header?: object, silent?: boolean }} options
|
||||||
* @returns {Promise<any>} 业务 data 字段(若无 data 则返回整包)
|
* @returns {Promise<any>} 业务 data 字段(若无 data 则返回整包)
|
||||||
@@ -24,7 +26,7 @@ export function request(options = {}) {
|
|||||||
headers.Authorization = `Bearer ${token}`
|
headers.Authorization = `Bearer ${token}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const fullUrl = BASE_URL.replace(/\/$/, '') + url
|
const fullUrl = buildFullUrl(url)
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
uni.request({
|
uni.request({
|
||||||
@@ -96,7 +98,7 @@ export function requestRaw(options = {}) {
|
|||||||
headers.Authorization = `Bearer ${token}`
|
headers.Authorization = `Bearer ${token}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const fullUrl = BASE_URL.replace(/\/$/, '') + url
|
const fullUrl = buildFullUrl(url)
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
uni.request({
|
uni.request({
|
||||||
|
|||||||
@@ -0,0 +1,531 @@
|
|||||||
|
<template>
|
||||||
|
<!-- #ifdef H5 -->
|
||||||
|
<div class="note-editor-h5">
|
||||||
|
<div class="editor-toolbar">
|
||||||
|
<div class="toolbar-group">
|
||||||
|
<button type="button" class="tb-btn" title="加粗" @mousedown.prevent="execFormat('bold')">
|
||||||
|
<FaIcon name="bold" :size="14" color="#606266" />
|
||||||
|
</button>
|
||||||
|
<button type="button" class="tb-btn" title="斜体" @mousedown.prevent="execFormat('italic')">
|
||||||
|
<FaIcon name="italic" :size="14" color="#606266" />
|
||||||
|
</button>
|
||||||
|
<button type="button" class="tb-btn" title="下划线" @mousedown.prevent="execFormat('underline')">
|
||||||
|
<FaIcon name="underline" :size="14" color="#606266" />
|
||||||
|
</button>
|
||||||
|
<button type="button" class="tb-btn" title="左对齐" @mousedown.prevent="execFormat('justifyLeft')">
|
||||||
|
<FaIcon name="align-left" :size="14" color="#606266" />
|
||||||
|
</button>
|
||||||
|
<button type="button" class="tb-btn" title="居中对齐" @mousedown.prevent="execFormat('justifyCenter')">
|
||||||
|
<FaIcon name="align-center" :size="14" color="#606266" />
|
||||||
|
</button>
|
||||||
|
<button type="button" class="tb-btn" title="右对齐" @mousedown.prevent="execFormat('justifyRight')">
|
||||||
|
<FaIcon name="align-right" :size="14" color="#606266" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="toolbar-divider" />
|
||||||
|
<div class="toolbar-group">
|
||||||
|
<button type="button" class="tb-btn" title="插入表格" @mousedown.prevent="onInsertTable">
|
||||||
|
<FaIcon name="table" :size="14" color="#606266" />
|
||||||
|
</button>
|
||||||
|
<button type="button" class="tb-btn" title="上方插入行" @mousedown.prevent="onAddRowBefore">
|
||||||
|
<FaIcon name="arrow-up" :size="12" color="#606266" />
|
||||||
|
<span class="tb-label">行</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="tb-btn" title="下方插入行" @mousedown.prevent="onAddRowAfter">
|
||||||
|
<FaIcon name="arrow-down" :size="12" color="#606266" />
|
||||||
|
<span class="tb-label">行</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="tb-btn" title="删除行" @mousedown.prevent="onDeleteRow">
|
||||||
|
<FaIcon name="minus" :size="12" color="#606266" />
|
||||||
|
<span class="tb-label">行</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="toolbar-divider" />
|
||||||
|
<div class="toolbar-group">
|
||||||
|
<button type="button" class="tb-btn" title="左侧插入列" @mousedown.prevent="onAddColBefore">
|
||||||
|
<FaIcon name="arrow-left" :size="12" color="#606266" />
|
||||||
|
<span class="tb-label">列</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="tb-btn" title="右侧插入列" @mousedown.prevent="onAddColAfter">
|
||||||
|
<FaIcon name="arrow-right" :size="12" color="#606266" />
|
||||||
|
<span class="tb-label">列</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="tb-btn" title="删除列" @mousedown.prevent="onDeleteCol">
|
||||||
|
<FaIcon name="minus" :size="12" color="#606266" />
|
||||||
|
<span class="tb-label">列</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="tb-btn tb-btn-danger" title="删除表格" @mousedown.prevent="onDeleteTable">
|
||||||
|
<FaIcon name="trash-can" :size="14" color="#f56c6c" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
ref="h5EditorRef"
|
||||||
|
class="note-rich-editor rich-content"
|
||||||
|
contenteditable="true"
|
||||||
|
:data-placeholder="placeholder"
|
||||||
|
@mousedown="onH5MouseDown"
|
||||||
|
@input="onH5Input"
|
||||||
|
@blur="onH5Input"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<!-- #endif -->
|
||||||
|
<!-- #ifndef H5 -->
|
||||||
|
<editor
|
||||||
|
:id="editorId"
|
||||||
|
class="note-rich-editor"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
@ready="onEditorReady"
|
||||||
|
@input="onEditorInput"
|
||||||
|
/>
|
||||||
|
<!-- #endif -->
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, watch, nextTick, getCurrentInstance, onMounted } from 'vue'
|
||||||
|
import FaIcon from '@/components/FaIcon.vue'
|
||||||
|
import {
|
||||||
|
buildTableHtml,
|
||||||
|
getCurrentCell,
|
||||||
|
addRowBefore,
|
||||||
|
addRowAfter,
|
||||||
|
deleteRow,
|
||||||
|
addColBefore,
|
||||||
|
addColAfter,
|
||||||
|
deleteCol
|
||||||
|
} from '@/utils/table-editor.js'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: '请输入内容'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
|
const editorId = 'noteRichEditor'
|
||||||
|
const h5EditorRef = ref(null)
|
||||||
|
const editorCtx = ref(null)
|
||||||
|
const instance = getCurrentInstance()
|
||||||
|
const isInternalUpdate = ref(false)
|
||||||
|
const lastAppliedHtml = ref('')
|
||||||
|
|
||||||
|
function normalizeEditorHtml(html) {
|
||||||
|
if (!html) return ''
|
||||||
|
const template = document.createElement('template')
|
||||||
|
template.innerHTML = html.trim()
|
||||||
|
const root = template.content
|
||||||
|
|
||||||
|
root.querySelectorAll('[contenteditable]').forEach((node) => {
|
||||||
|
node.removeAttribute('contenteditable')
|
||||||
|
})
|
||||||
|
|
||||||
|
root.querySelectorAll('.column-resize-handle, .selectedCell, .grip-column, .grip-row').forEach((node) => {
|
||||||
|
node.remove()
|
||||||
|
})
|
||||||
|
|
||||||
|
root.querySelectorAll('td, th').forEach((cell) => {
|
||||||
|
const text = cell.textContent || ''
|
||||||
|
if (!text.trim() && !cell.querySelector('img, br')) {
|
||||||
|
cell.innerHTML = '<p><br></p>'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return template.innerHTML
|
||||||
|
}
|
||||||
|
|
||||||
|
function unlockEditableTree(root) {
|
||||||
|
if (!root) return
|
||||||
|
root.querySelectorAll('[contenteditable="false"]').forEach((node) => {
|
||||||
|
node.removeAttribute('contenteditable')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function patchTables(root) {
|
||||||
|
if (!root) return
|
||||||
|
root.querySelectorAll('table').forEach((table) => {
|
||||||
|
table.removeAttribute('contenteditable')
|
||||||
|
table.style.borderCollapse = 'collapse'
|
||||||
|
table.style.border = '1px solid #e4e7ed'
|
||||||
|
table.style.width = '100%'
|
||||||
|
table.style.margin = '12px 0'
|
||||||
|
table.querySelectorAll('th, td').forEach((cell) => {
|
||||||
|
cell.removeAttribute('contenteditable')
|
||||||
|
cell.style.border = '1px solid #e4e7ed'
|
||||||
|
cell.style.padding = '8px 12px'
|
||||||
|
cell.style.verticalAlign = 'top'
|
||||||
|
cell.style.wordBreak = 'break-word'
|
||||||
|
cell.style.userSelect = 'text'
|
||||||
|
cell.style.cursor = 'text'
|
||||||
|
})
|
||||||
|
table.querySelectorAll('th').forEach((cell) => {
|
||||||
|
cell.style.backgroundColor = '#f5f7fa'
|
||||||
|
cell.style.fontWeight = '600'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function placeCaretInCell(cell, event) {
|
||||||
|
const el = h5EditorRef.value
|
||||||
|
if (!el || !cell) return
|
||||||
|
el.focus()
|
||||||
|
|
||||||
|
let range = null
|
||||||
|
if (event && document.caretRangeFromPoint) {
|
||||||
|
range = document.caretRangeFromPoint(event.clientX, event.clientY)
|
||||||
|
} else if (event && document.caretPositionFromPoint) {
|
||||||
|
const pos = document.caretPositionFromPoint(event.clientX, event.clientY)
|
||||||
|
if (pos) {
|
||||||
|
range = document.createRange()
|
||||||
|
range.setStart(pos.offsetNode, pos.offset)
|
||||||
|
range.collapse(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (range && cell.contains(range.startContainer)) {
|
||||||
|
const sel = window.getSelection()
|
||||||
|
sel.removeAllRanges()
|
||||||
|
sel.addRange(range)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const fallback = document.createRange()
|
||||||
|
const target = cell.querySelector('p, div, span') || cell
|
||||||
|
fallback.selectNodeContents(target)
|
||||||
|
fallback.collapse(false)
|
||||||
|
const sel = window.getSelection()
|
||||||
|
sel.removeAllRanges()
|
||||||
|
sel.addRange(fallback)
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncContent() {
|
||||||
|
const el = h5EditorRef.value
|
||||||
|
if (!el) return
|
||||||
|
unlockEditableTree(el)
|
||||||
|
patchTables(el)
|
||||||
|
const html = el.innerHTML || ''
|
||||||
|
lastAppliedHtml.value = html
|
||||||
|
isInternalUpdate.value = true
|
||||||
|
emit('update:modelValue', html)
|
||||||
|
nextTick(() => {
|
||||||
|
isInternalUpdate.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function insertHtmlAtCursor(html) {
|
||||||
|
const el = h5EditorRef.value
|
||||||
|
if (!el) return
|
||||||
|
el.focus()
|
||||||
|
if (document.queryCommandSupported('insertHTML')) {
|
||||||
|
document.execCommand('insertHTML', false, html)
|
||||||
|
} else {
|
||||||
|
el.insertAdjacentHTML('beforeend', html)
|
||||||
|
}
|
||||||
|
syncContent()
|
||||||
|
}
|
||||||
|
|
||||||
|
function execFormat(cmd) {
|
||||||
|
const el = h5EditorRef.value
|
||||||
|
if (!el) return
|
||||||
|
el.focus()
|
||||||
|
document.execCommand(cmd, false, null)
|
||||||
|
syncContent()
|
||||||
|
}
|
||||||
|
|
||||||
|
function requireCell(message = '请先在表格中选中单元格') {
|
||||||
|
const cell = getCurrentCell(h5EditorRef.value)
|
||||||
|
if (!cell) {
|
||||||
|
uni.showToast({ title: message, icon: 'none' })
|
||||||
|
}
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
function onInsertTable() {
|
||||||
|
uni.showActionSheet({
|
||||||
|
itemList: ['3×3 表格', '4×4 表格', '5×3 表格'],
|
||||||
|
success: (res) => {
|
||||||
|
const sizes = [[3, 3], [4, 4], [5, 3]]
|
||||||
|
const [rows, cols] = sizes[res.tapIndex] || [3, 3]
|
||||||
|
insertHtmlAtCursor(buildTableHtml(rows, cols))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function onAddRowBefore() {
|
||||||
|
const cell = requireCell()
|
||||||
|
if (!cell) return
|
||||||
|
const target = addRowBefore(cell)
|
||||||
|
syncContent()
|
||||||
|
if (target) placeCaretInCell(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onAddRowAfter() {
|
||||||
|
const cell = requireCell()
|
||||||
|
if (!cell) return
|
||||||
|
const target = addRowAfter(cell)
|
||||||
|
syncContent()
|
||||||
|
if (target) placeCaretInCell(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDeleteRow() {
|
||||||
|
const cell = requireCell()
|
||||||
|
if (!cell) return
|
||||||
|
const target = deleteRow(cell)
|
||||||
|
if (!target) {
|
||||||
|
uni.showToast({ title: '至少保留一行', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
syncContent()
|
||||||
|
placeCaretInCell(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onAddColBefore() {
|
||||||
|
const cell = requireCell()
|
||||||
|
if (!cell) return
|
||||||
|
const target = addColBefore(cell)
|
||||||
|
syncContent()
|
||||||
|
if (target) placeCaretInCell(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onAddColAfter() {
|
||||||
|
const cell = requireCell()
|
||||||
|
if (!cell) return
|
||||||
|
const target = addColAfter(cell)
|
||||||
|
syncContent()
|
||||||
|
if (target) placeCaretInCell(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDeleteCol() {
|
||||||
|
const cell = requireCell()
|
||||||
|
if (!cell) return
|
||||||
|
const target = deleteCol(cell)
|
||||||
|
if (!target) {
|
||||||
|
uni.showToast({ title: '至少保留一列', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
syncContent()
|
||||||
|
placeCaretInCell(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDeleteTable() {
|
||||||
|
const cell = requireCell()
|
||||||
|
if (!cell) return
|
||||||
|
const table = cell.closest('table')
|
||||||
|
if (!table) return
|
||||||
|
uni.showModal({
|
||||||
|
title: '删除表格',
|
||||||
|
content: '确定删除当前表格吗?',
|
||||||
|
success: (res) => {
|
||||||
|
if (!res.confirm) return
|
||||||
|
table.remove()
|
||||||
|
syncContent()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function onH5MouseDown(e) {
|
||||||
|
const el = h5EditorRef.value
|
||||||
|
if (!el) return
|
||||||
|
const cell = e.target.closest('td, th')
|
||||||
|
if (!cell || !el.contains(cell)) return
|
||||||
|
|
||||||
|
unlockEditableTree(el)
|
||||||
|
const evt = e
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
placeCaretInCell(cell, evt)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function setH5Html(html) {
|
||||||
|
// #ifdef H5
|
||||||
|
nextTick(() => {
|
||||||
|
const el = h5EditorRef.value
|
||||||
|
if (!el) return
|
||||||
|
const normalized = normalizeEditorHtml(html || '')
|
||||||
|
if (normalized === lastAppliedHtml.value && el.innerHTML === normalized) {
|
||||||
|
patchTables(el)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (el.innerHTML !== normalized) {
|
||||||
|
el.innerHTML = normalized
|
||||||
|
}
|
||||||
|
unlockEditableTree(el)
|
||||||
|
patchTables(el)
|
||||||
|
lastAppliedHtml.value = normalized
|
||||||
|
})
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
|
||||||
|
function onH5Input(e) {
|
||||||
|
// #ifdef H5
|
||||||
|
const html = e.target.innerHTML || ''
|
||||||
|
lastAppliedHtml.value = html
|
||||||
|
isInternalUpdate.value = true
|
||||||
|
emit('update:modelValue', html)
|
||||||
|
nextTick(() => {
|
||||||
|
isInternalUpdate.value = false
|
||||||
|
})
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
|
||||||
|
function onEditorInput(e) {
|
||||||
|
emit('update:modelValue', e.detail.html || '')
|
||||||
|
}
|
||||||
|
|
||||||
|
function onEditorReady() {
|
||||||
|
// #ifndef H5
|
||||||
|
uni.createSelectorQuery()
|
||||||
|
.in(instance)
|
||||||
|
.select(`#${editorId}`)
|
||||||
|
.context((res) => {
|
||||||
|
if (res && res.context) {
|
||||||
|
editorCtx.value = res.context
|
||||||
|
if (props.modelValue) {
|
||||||
|
editorCtx.value.setContents({ html: props.modelValue })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.exec()
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(val) => {
|
||||||
|
if (isInternalUpdate.value) return
|
||||||
|
// #ifdef H5
|
||||||
|
setH5Html(val)
|
||||||
|
// #endif
|
||||||
|
// #ifndef H5
|
||||||
|
if (editorCtx.value) {
|
||||||
|
editorCtx.value.setContents({ html: val || '' })
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// #ifdef H5
|
||||||
|
setH5Html(props.modelValue)
|
||||||
|
// #endif
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.note-editor-h5 {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 360rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-toolbar {
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8rpx;
|
||||||
|
padding-bottom: 12rpx;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
border-bottom: 1rpx solid #ebeef5;
|
||||||
|
overflow-x: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar-group {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar-divider {
|
||||||
|
width: 1rpx;
|
||||||
|
height: 28rpx;
|
||||||
|
background: #ebeef5;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tb-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 4rpx;
|
||||||
|
min-width: 56rpx;
|
||||||
|
height: 56rpx;
|
||||||
|
padding: 0 12rpx;
|
||||||
|
border: 1rpx solid #ebeef5;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
background: #fff;
|
||||||
|
color: #606266;
|
||||||
|
font-size: 22rpx;
|
||||||
|
line-height: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: #f5f7fa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tb-btn-danger {
|
||||||
|
border-color: #fde2e2;
|
||||||
|
background: #fef0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tb-label {
|
||||||
|
font-size: 20rpx;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-rich-editor {
|
||||||
|
width: 100%;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 28rpx;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: #303133;
|
||||||
|
outline: none;
|
||||||
|
overflow-y: auto;
|
||||||
|
-webkit-user-select: text;
|
||||||
|
user-select: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* #ifdef H5 */
|
||||||
|
.note-rich-editor:empty::before {
|
||||||
|
content: attr(data-placeholder);
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-rich-editor:focus:empty::before {
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
/* #endif */
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import '@/src/styles/rich-content.scss';
|
||||||
|
|
||||||
|
.note-rich-editor.rich-content {
|
||||||
|
table {
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
user-select: text !important;
|
||||||
|
-webkit-user-select: text !important;
|
||||||
|
cursor: text !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
min-height: 1.4em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Generated
+12240
-92
File diff suppressed because it is too large
Load Diff
+65
-2
@@ -1,13 +1,76 @@
|
|||||||
{
|
{
|
||||||
|
"name": "uniapp",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"scripts": {
|
||||||
|
"dev:custom": "cross-env UNI_INPUT_DIR=. uni -p",
|
||||||
|
"dev:h5": "cross-env UNI_INPUT_DIR=. uni",
|
||||||
|
"dev:h5:ssr": "cross-env UNI_INPUT_DIR=. uni --ssr",
|
||||||
|
"dev:mp-alipay": "cross-env UNI_INPUT_DIR=. uni -p mp-alipay",
|
||||||
|
"dev:mp-baidu": "cross-env UNI_INPUT_DIR=. uni -p mp-baidu",
|
||||||
|
"dev:mp-jd": "cross-env UNI_INPUT_DIR=. uni -p mp-jd",
|
||||||
|
"dev:mp-kuaishou": "cross-env UNI_INPUT_DIR=. uni -p mp-kuaishou",
|
||||||
|
"dev:mp-lark": "cross-env UNI_INPUT_DIR=. uni -p mp-lark",
|
||||||
|
"dev:mp-qq": "cross-env UNI_INPUT_DIR=. uni -p mp-qq",
|
||||||
|
"dev:mp-toutiao": "cross-env UNI_INPUT_DIR=. uni -p mp-toutiao",
|
||||||
|
"dev:mp-harmony": "cross-env UNI_INPUT_DIR=. uni -p mp-harmony",
|
||||||
|
"dev:mp-weixin": "cross-env UNI_INPUT_DIR=. uni -p mp-weixin",
|
||||||
|
"dev:mp-xhs": "cross-env UNI_INPUT_DIR=. uni -p mp-xhs",
|
||||||
|
"dev:quickapp-webview": "cross-env UNI_INPUT_DIR=. uni -p quickapp-webview",
|
||||||
|
"dev:quickapp-webview-huawei": "cross-env UNI_INPUT_DIR=. uni -p quickapp-webview-huawei",
|
||||||
|
"dev:quickapp-webview-union": "cross-env UNI_INPUT_DIR=. uni -p quickapp-webview-union",
|
||||||
|
"build:custom": "cross-env UNI_INPUT_DIR=. uni build -p",
|
||||||
|
"build:h5": "cross-env UNI_INPUT_DIR=. uni build",
|
||||||
|
"build:h5:ssr": "cross-env UNI_INPUT_DIR=. uni build --ssr",
|
||||||
|
"build:mp-alipay": "cross-env UNI_INPUT_DIR=. uni build -p mp-alipay",
|
||||||
|
"build:mp-baidu": "cross-env UNI_INPUT_DIR=. uni build -p mp-baidu",
|
||||||
|
"build:mp-jd": "cross-env UNI_INPUT_DIR=. uni build -p mp-jd",
|
||||||
|
"build:mp-kuaishou": "cross-env UNI_INPUT_DIR=. uni build -p mp-kuaishou",
|
||||||
|
"build:mp-lark": "cross-env UNI_INPUT_DIR=. uni build -p mp-lark",
|
||||||
|
"build:mp-qq": "cross-env UNI_INPUT_DIR=. uni build -p mp-qq",
|
||||||
|
"build:mp-toutiao": "cross-env UNI_INPUT_DIR=. uni build -p mp-toutiao",
|
||||||
|
"build:mp-harmony": "cross-env UNI_INPUT_DIR=. uni build -p mp-harmony",
|
||||||
|
"build:mp-weixin": "cross-env UNI_INPUT_DIR=. uni build -p mp-weixin",
|
||||||
|
"build:mp-xhs": "cross-env UNI_INPUT_DIR=. uni build -p mp-xhs",
|
||||||
|
"build:quickapp-webview": "cross-env UNI_INPUT_DIR=. uni build -p quickapp-webview",
|
||||||
|
"build:quickapp-webview-huawei": "cross-env UNI_INPUT_DIR=. uni build -p quickapp-webview-huawei",
|
||||||
|
"build:quickapp-webview-union": "cross-env UNI_INPUT_DIR=. uni build -p quickapp-webview-union"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@dcloudio/uni-app": "3.0.0-5020420260813002",
|
||||||
|
"@dcloudio/uni-app-harmony": "3.0.0-5020420260813002",
|
||||||
|
"@dcloudio/uni-app-plus": "3.0.0-5020420260813002",
|
||||||
|
"@dcloudio/uni-components": "3.0.0-5020420260813002",
|
||||||
|
"@dcloudio/uni-h5": "3.0.0-5020420260813002",
|
||||||
|
"@dcloudio/uni-mp-alipay": "3.0.0-5020420260813002",
|
||||||
|
"@dcloudio/uni-mp-baidu": "3.0.0-5020420260813002",
|
||||||
|
"@dcloudio/uni-mp-harmony": "3.0.0-5020420260813002",
|
||||||
|
"@dcloudio/uni-mp-jd": "3.0.0-5020420260813002",
|
||||||
|
"@dcloudio/uni-mp-kuaishou": "3.0.0-5020420260813002",
|
||||||
|
"@dcloudio/uni-mp-lark": "3.0.0-5020420260813002",
|
||||||
|
"@dcloudio/uni-mp-qq": "3.0.0-5020420260813002",
|
||||||
|
"@dcloudio/uni-mp-toutiao": "3.0.0-5020420260813002",
|
||||||
|
"@dcloudio/uni-mp-weixin": "3.0.0-5020420260813002",
|
||||||
|
"@dcloudio/uni-mp-xhs": "3.0.0-5020420260813002",
|
||||||
|
"@dcloudio/uni-quickapp-webview": "3.0.0-5020420260813002",
|
||||||
"@fortawesome/fontawesome-free": "^7.3.0",
|
"@fortawesome/fontawesome-free": "^7.3.0",
|
||||||
"clipboard": "^2.0.11",
|
"clipboard": "^2.0.11",
|
||||||
"dayjs": "^1.11.21",
|
"dayjs": "^1.11.21",
|
||||||
"uview-plus": "^3.8.55"
|
"uview-plus": "^3.8.55",
|
||||||
|
"vue": "^3.4.21",
|
||||||
|
"vue-i18n": "^9.1.9"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@dcloudio/types": "^3.4.8",
|
||||||
|
"@dcloudio/uni-automator": "3.0.0-5020420260813002",
|
||||||
|
"@dcloudio/uni-cli-shared": "3.0.0-5020420260813002",
|
||||||
|
"@dcloudio/uni-stacktracey": "3.0.0-5020420260813002",
|
||||||
|
"@dcloudio/vite-plugin-uni": "3.0.0-5020420260813002",
|
||||||
|
"@vue/runtime-core": "^3.4.21",
|
||||||
|
"cross-env": "^7.0.3",
|
||||||
|
"rollup": "4.14.3",
|
||||||
"rollup-plugin-visualizer": "^7.0.1",
|
"rollup-plugin-visualizer": "^7.0.1",
|
||||||
"sass": "^1.63.2",
|
"sass": "^1.63.2",
|
||||||
"sass-loader": "^10.4.1"
|
"sass-loader": "^10.4.1",
|
||||||
|
"vite": "5.2.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
"easycom": {
|
"easycom": {
|
||||||
"autoscan": true,
|
"autoscan": true,
|
||||||
"custom": {
|
"custom": {
|
||||||
"^FaIcon$": "@/components/FaIcon.vue",
|
|
||||||
"^u--(.*)": "uview-plus/components/u-$1/u-$1.vue",
|
"^u--(.*)": "uview-plus/components/u-$1/u-$1.vue",
|
||||||
"^up-(.*)": "uview-plus/components/u-$1/u-$1.vue",
|
"^up-(.*)": "uview-plus/components/u-$1/u-$1.vue",
|
||||||
"^u-([^-].*)": "uview-plus/components/u-$1/u-$1.vue"
|
"^u-([^-].*)": "uview-plus/components/u-$1/u-$1.vue"
|
||||||
|
|||||||
@@ -17,23 +17,21 @@
|
|||||||
<!-- 第一步:验证租户和账号 -->
|
<!-- 第一步:验证租户和账号 -->
|
||||||
<view v-if="currentStep === 1" class="form">
|
<view v-if="currentStep === 1" class="form">
|
||||||
<view class="field">
|
<view class="field">
|
||||||
<u-input
|
<input
|
||||||
|
class="field-input"
|
||||||
v-model="form.tenant_name"
|
v-model="form.tenant_name"
|
||||||
|
type="text"
|
||||||
placeholder="租户名称"
|
placeholder="租户名称"
|
||||||
border="none"
|
placeholder-class="field-placeholder"
|
||||||
color="#303133"
|
|
||||||
:customStyle="inputStyle"
|
|
||||||
:placeholderStyle="placeholderStyle"
|
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
<view class="field">
|
<view class="field">
|
||||||
<u-input
|
<input
|
||||||
|
class="field-input"
|
||||||
v-model="form.account"
|
v-model="form.account"
|
||||||
|
type="text"
|
||||||
placeholder="账号"
|
placeholder="账号"
|
||||||
border="none"
|
placeholder-class="field-placeholder"
|
||||||
color="#303133"
|
|
||||||
:customStyle="inputStyle"
|
|
||||||
:placeholderStyle="placeholderStyle"
|
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -54,15 +52,13 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-if="verifyResult.phone" class="field">
|
<view v-if="verifyResult.phone" class="field">
|
||||||
<u-input
|
<input
|
||||||
|
class="field-input"
|
||||||
v-model="form.phone"
|
v-model="form.phone"
|
||||||
placeholder="手机号"
|
type="tel"
|
||||||
type="number"
|
|
||||||
maxlength="11"
|
maxlength="11"
|
||||||
border="none"
|
placeholder="手机号"
|
||||||
color="#303133"
|
placeholder-class="field-placeholder"
|
||||||
:customStyle="inputStyle"
|
|
||||||
:placeholderStyle="placeholderStyle"
|
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
<view v-else class="empty-contact">
|
<view v-else class="empty-contact">
|
||||||
@@ -70,15 +66,13 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="field field-row">
|
<view class="field field-row">
|
||||||
<u-input
|
<input
|
||||||
|
class="field-input"
|
||||||
v-model="form.sms_code"
|
v-model="form.sms_code"
|
||||||
placeholder="短信验证码"
|
|
||||||
type="number"
|
type="number"
|
||||||
maxlength="6"
|
maxlength="6"
|
||||||
border="none"
|
placeholder="短信验证码"
|
||||||
color="#303133"
|
placeholder-class="field-placeholder"
|
||||||
:customStyle="inputStyle"
|
|
||||||
:placeholderStyle="placeholderStyle"
|
|
||||||
/>
|
/>
|
||||||
<text
|
<text
|
||||||
class="code-link"
|
class="code-link"
|
||||||
@@ -109,25 +103,21 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="field">
|
<view class="field">
|
||||||
<u-input
|
<input
|
||||||
|
class="field-input"
|
||||||
v-model="form.new_password"
|
v-model="form.new_password"
|
||||||
placeholder="新密码"
|
|
||||||
type="password"
|
type="password"
|
||||||
border="none"
|
placeholder="新密码"
|
||||||
color="#303133"
|
placeholder-class="field-placeholder"
|
||||||
:customStyle="inputStyle"
|
|
||||||
:placeholderStyle="placeholderStyle"
|
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
<view class="field">
|
<view class="field">
|
||||||
<u-input
|
<input
|
||||||
|
class="field-input"
|
||||||
v-model="form.confirm_password"
|
v-model="form.confirm_password"
|
||||||
placeholder="确认新密码"
|
|
||||||
type="password"
|
type="password"
|
||||||
border="none"
|
placeholder="确认新密码"
|
||||||
color="#303133"
|
placeholder-class="field-placeholder"
|
||||||
:customStyle="inputStyle"
|
|
||||||
:placeholderStyle="placeholderStyle"
|
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -170,14 +160,6 @@ const verifyResult = reactive({
|
|||||||
email: ''
|
email: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
const inputStyle = {
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
padding: '0 8rpx',
|
|
||||||
height: '96rpx',
|
|
||||||
fontSize: '28rpx'
|
|
||||||
}
|
|
||||||
const placeholderStyle = 'color: #909399; font-size: 28rpx'
|
|
||||||
|
|
||||||
function startCountdown() {
|
function startCountdown() {
|
||||||
countdown.value = 60
|
countdown.value = 60
|
||||||
if (timer) clearInterval(timer)
|
if (timer) clearInterval(timer)
|
||||||
@@ -386,12 +368,35 @@ onUnmounted(() => {
|
|||||||
background: $color-bg-page;
|
background: $color-bg-page;
|
||||||
border-radius: $radius-md;
|
border-radius: $radius-md;
|
||||||
padding: 0 24rpx;
|
padding: 0 24rpx;
|
||||||
overflow: hidden;
|
min-height: 96rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-input {
|
||||||
|
width: 100%;
|
||||||
|
height: 96rpx;
|
||||||
|
line-height: 96rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: $color-text;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-placeholder {
|
||||||
|
color: $color-text-muted;
|
||||||
|
font-size: 32rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.field-row {
|
.field-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
.field-input {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.code-link {
|
.code-link {
|
||||||
|
|||||||
@@ -22,43 +22,35 @@
|
|||||||
<view class="form-card">
|
<view class="form-card">
|
||||||
<view class="form">
|
<view class="form">
|
||||||
<view class="field">
|
<view class="field">
|
||||||
<u-input
|
<input
|
||||||
|
class="field-input"
|
||||||
v-model="tenantName"
|
v-model="tenantName"
|
||||||
|
type="text"
|
||||||
placeholder="请输入租户名称"
|
placeholder="请输入租户名称"
|
||||||
border="none"
|
placeholder-class="field-placeholder"
|
||||||
color="#303133"
|
|
||||||
:customStyle="inputStyle"
|
|
||||||
:placeholderStyle="placeholderStyle"
|
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<template v-if="loginType === 'phone'">
|
<template v-if="loginType === 'phone'">
|
||||||
<view class="field">
|
<view class="field field-phone">
|
||||||
<u-input
|
|
||||||
v-model="phone"
|
|
||||||
placeholder="请输入手机号"
|
|
||||||
type="number"
|
|
||||||
maxlength="11"
|
|
||||||
border="none"
|
|
||||||
color="#303133"
|
|
||||||
:customStyle="inputStyle"
|
|
||||||
:placeholderStyle="placeholderStyle"
|
|
||||||
>
|
|
||||||
<template #prefix>
|
|
||||||
<text class="prefix">+86</text>
|
<text class="prefix">+86</text>
|
||||||
</template>
|
<input
|
||||||
</u-input>
|
class="field-input"
|
||||||
|
v-model="phone"
|
||||||
|
type="tel"
|
||||||
|
maxlength="11"
|
||||||
|
placeholder="请输入手机号"
|
||||||
|
placeholder-class="field-placeholder"
|
||||||
|
/>
|
||||||
</view>
|
</view>
|
||||||
<view class="field field-row">
|
<view class="field field-row">
|
||||||
<u-input
|
<input
|
||||||
|
class="field-input"
|
||||||
v-model="smsCode"
|
v-model="smsCode"
|
||||||
placeholder="请输入验证码"
|
|
||||||
type="number"
|
type="number"
|
||||||
maxlength="6"
|
maxlength="6"
|
||||||
border="none"
|
placeholder="请输入验证码"
|
||||||
color="#303133"
|
placeholder-class="field-placeholder"
|
||||||
:customStyle="inputStyle"
|
|
||||||
:placeholderStyle="placeholderStyle"
|
|
||||||
/>
|
/>
|
||||||
<text
|
<text
|
||||||
class="code-link"
|
class="code-link"
|
||||||
@@ -70,37 +62,31 @@
|
|||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<view class="field">
|
<view class="field">
|
||||||
<u-input
|
<input
|
||||||
|
class="field-input"
|
||||||
v-model="username"
|
v-model="username"
|
||||||
|
type="text"
|
||||||
placeholder="请输入账号"
|
placeholder="请输入账号"
|
||||||
border="none"
|
placeholder-class="field-placeholder"
|
||||||
color="#303133"
|
|
||||||
:customStyle="inputStyle"
|
|
||||||
:placeholderStyle="placeholderStyle"
|
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
<view class="field">
|
<view class="field">
|
||||||
<u-input
|
<input
|
||||||
|
class="field-input"
|
||||||
v-model="password"
|
v-model="password"
|
||||||
placeholder="请输入密码"
|
|
||||||
type="password"
|
type="password"
|
||||||
border="none"
|
placeholder="请输入密码"
|
||||||
color="#303133"
|
placeholder-class="field-placeholder"
|
||||||
:customStyle="inputStyle"
|
|
||||||
:placeholderStyle="placeholderStyle"
|
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
<!-- 后台开启短信/邮箱登录校验时展示 -->
|
|
||||||
<view v-if="needLoginCode" class="field field-row">
|
<view v-if="needLoginCode" class="field field-row">
|
||||||
<u-input
|
<input
|
||||||
|
class="field-input"
|
||||||
v-model="verifyCode"
|
v-model="verifyCode"
|
||||||
placeholder="请输入登录验证码"
|
|
||||||
type="number"
|
type="number"
|
||||||
maxlength="8"
|
maxlength="8"
|
||||||
border="none"
|
placeholder="请输入登录验证码"
|
||||||
color="#303133"
|
placeholder-class="field-placeholder"
|
||||||
:customStyle="inputStyle"
|
|
||||||
:placeholderStyle="placeholderStyle"
|
|
||||||
/>
|
/>
|
||||||
<text
|
<text
|
||||||
class="code-link"
|
class="code-link"
|
||||||
@@ -180,14 +166,6 @@ const openVerify = ref(false)
|
|||||||
const verifyType = ref('')
|
const verifyType = ref('')
|
||||||
let timer = null
|
let timer = null
|
||||||
|
|
||||||
const inputStyle = {
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
padding: '0 8rpx',
|
|
||||||
height: '96rpx',
|
|
||||||
fontSize: '28rpx'
|
|
||||||
}
|
|
||||||
const placeholderStyle = 'color: #909399; font-size: 28rpx'
|
|
||||||
|
|
||||||
const needLoginCode = computed(() => {
|
const needLoginCode = computed(() => {
|
||||||
return openVerify.value && (verifyType.value === 'sms' || verifyType.value === 'email')
|
return openVerify.value && (verifyType.value === 'sms' || verifyType.value === 'email')
|
||||||
})
|
})
|
||||||
@@ -428,7 +406,8 @@ function goForget() {
|
|||||||
|
|
||||||
.page-inner {
|
.page-inner {
|
||||||
padding: 0 48rpx;
|
padding: 0 48rpx;
|
||||||
padding-top: calc(var(--status-bar-height, 44px) + 80rpx);
|
padding-top: calc(env(safe-area-inset-top, 0px) + 80rpx);
|
||||||
|
padding-bottom: env(safe-area-inset-bottom, 0px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
@@ -488,12 +467,40 @@ function goForget() {
|
|||||||
background: $color-bg-page;
|
background: $color-bg-page;
|
||||||
border-radius: $radius-md;
|
border-radius: $radius-md;
|
||||||
padding: 0 24rpx;
|
padding: 0 24rpx;
|
||||||
overflow: hidden;
|
min-height: 96rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-input {
|
||||||
|
width: 100%;
|
||||||
|
height: 96rpx;
|
||||||
|
line-height: 96rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: $color-text;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-placeholder {
|
||||||
|
color: $color-text-muted;
|
||||||
|
font-size: 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-phone {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.field-row {
|
.field-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
.field-input {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.prefix {
|
.prefix {
|
||||||
|
|||||||
@@ -14,57 +14,50 @@
|
|||||||
<view class="form-card">
|
<view class="form-card">
|
||||||
<view class="form">
|
<view class="form">
|
||||||
<view class="field">
|
<view class="field">
|
||||||
<u-input
|
<input
|
||||||
|
class="field-input"
|
||||||
v-model="form.tenant_name"
|
v-model="form.tenant_name"
|
||||||
|
type="text"
|
||||||
placeholder="租户名称"
|
placeholder="租户名称"
|
||||||
border="none"
|
placeholder-class="field-placeholder"
|
||||||
color="#303133"
|
|
||||||
:customStyle="inputStyle"
|
|
||||||
:placeholderStyle="placeholderStyle"
|
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
<view class="field">
|
<view class="field">
|
||||||
<u-input
|
<input
|
||||||
|
class="field-input"
|
||||||
v-model="form.account"
|
v-model="form.account"
|
||||||
|
type="text"
|
||||||
placeholder="账号"
|
placeholder="账号"
|
||||||
border="none"
|
placeholder-class="field-placeholder"
|
||||||
color="#303133"
|
|
||||||
:customStyle="inputStyle"
|
|
||||||
:placeholderStyle="placeholderStyle"
|
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
<view class="field">
|
<view class="field">
|
||||||
<u-input
|
<input
|
||||||
|
class="field-input"
|
||||||
v-model="form.name"
|
v-model="form.name"
|
||||||
|
type="text"
|
||||||
placeholder="姓名"
|
placeholder="姓名"
|
||||||
border="none"
|
placeholder-class="field-placeholder"
|
||||||
color="#303133"
|
|
||||||
:customStyle="inputStyle"
|
|
||||||
:placeholderStyle="placeholderStyle"
|
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
<view class="field">
|
<view class="field">
|
||||||
<u-input
|
<input
|
||||||
|
class="field-input"
|
||||||
v-model="form.phone"
|
v-model="form.phone"
|
||||||
placeholder="手机号"
|
type="tel"
|
||||||
type="number"
|
|
||||||
maxlength="11"
|
maxlength="11"
|
||||||
border="none"
|
placeholder="手机号"
|
||||||
color="#303133"
|
placeholder-class="field-placeholder"
|
||||||
:customStyle="inputStyle"
|
|
||||||
:placeholderStyle="placeholderStyle"
|
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
<view class="field field-row">
|
<view class="field field-row">
|
||||||
<u-input
|
<input
|
||||||
|
class="field-input"
|
||||||
v-model="form.sms_code"
|
v-model="form.sms_code"
|
||||||
placeholder="短信验证码"
|
|
||||||
type="number"
|
type="number"
|
||||||
maxlength="6"
|
maxlength="6"
|
||||||
border="none"
|
placeholder="短信验证码"
|
||||||
color="#303133"
|
placeholder-class="field-placeholder"
|
||||||
:customStyle="inputStyle"
|
|
||||||
:placeholderStyle="placeholderStyle"
|
|
||||||
/>
|
/>
|
||||||
<text
|
<text
|
||||||
class="code-link"
|
class="code-link"
|
||||||
@@ -73,35 +66,30 @@
|
|||||||
>{{ countdown > 0 ? `${countdown}s` : (codeLoading ? '发送中' : '获取验证码') }}</text>
|
>{{ countdown > 0 ? `${countdown}s` : (codeLoading ? '发送中' : '获取验证码') }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="field">
|
<view class="field">
|
||||||
<u-input
|
<input
|
||||||
|
class="field-input"
|
||||||
v-model="form.email"
|
v-model="form.email"
|
||||||
|
type="text"
|
||||||
placeholder="邮箱(可选)"
|
placeholder="邮箱(可选)"
|
||||||
border="none"
|
placeholder-class="field-placeholder"
|
||||||
color="#303133"
|
|
||||||
:customStyle="inputStyle"
|
|
||||||
:placeholderStyle="placeholderStyle"
|
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
<view class="field">
|
<view class="field">
|
||||||
<u-input
|
<input
|
||||||
|
class="field-input"
|
||||||
v-model="form.password"
|
v-model="form.password"
|
||||||
placeholder="密码"
|
|
||||||
type="password"
|
type="password"
|
||||||
border="none"
|
placeholder="密码"
|
||||||
color="#303133"
|
placeholder-class="field-placeholder"
|
||||||
:customStyle="inputStyle"
|
|
||||||
:placeholderStyle="placeholderStyle"
|
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
<view class="field">
|
<view class="field">
|
||||||
<u-input
|
<input
|
||||||
|
class="field-input"
|
||||||
v-model="form.confirm_password"
|
v-model="form.confirm_password"
|
||||||
placeholder="确认密码"
|
|
||||||
type="password"
|
type="password"
|
||||||
border="none"
|
placeholder="确认密码"
|
||||||
color="#303133"
|
placeholder-class="field-placeholder"
|
||||||
:customStyle="inputStyle"
|
|
||||||
:placeholderStyle="placeholderStyle"
|
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -138,14 +126,6 @@ const form = reactive({
|
|||||||
confirm_password: ''
|
confirm_password: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
const inputStyle = {
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
padding: '0 8rpx',
|
|
||||||
height: '96rpx',
|
|
||||||
fontSize: '28rpx'
|
|
||||||
}
|
|
||||||
const placeholderStyle = 'color: #909399; font-size: 28rpx'
|
|
||||||
|
|
||||||
function startCountdown() {
|
function startCountdown() {
|
||||||
countdown.value = 60
|
countdown.value = 60
|
||||||
if (timer) clearInterval(timer)
|
if (timer) clearInterval(timer)
|
||||||
@@ -291,12 +271,35 @@ onUnmounted(() => {
|
|||||||
background: $color-bg-page;
|
background: $color-bg-page;
|
||||||
border-radius: $radius-md;
|
border-radius: $radius-md;
|
||||||
padding: 0 24rpx;
|
padding: 0 24rpx;
|
||||||
overflow: hidden;
|
min-height: 96rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-input {
|
||||||
|
width: 100%;
|
||||||
|
height: 96rpx;
|
||||||
|
line-height: 96rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: $color-text;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-placeholder {
|
||||||
|
color: $color-text-muted;
|
||||||
|
font-size: 32rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.field-row {
|
.field-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
.field-input {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.code-link {
|
.code-link {
|
||||||
|
|||||||
@@ -15,13 +15,10 @@
|
|||||||
|
|
||||||
<view class="field content-field">
|
<view class="field content-field">
|
||||||
<text class="field-label">内容</text>
|
<text class="field-label">内容</text>
|
||||||
<editor
|
<NoteRichEditor
|
||||||
id="noteEditor"
|
v-model="form.content"
|
||||||
class="note-editor"
|
class="note-editor"
|
||||||
:placeholder="'记录你的想法...'"
|
placeholder="记录你的想法..."
|
||||||
:value="form.content"
|
|
||||||
@ready="onEditorReady"
|
|
||||||
@input="onEditorInput"
|
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -52,10 +49,10 @@ import { ref, reactive } from 'vue'
|
|||||||
import { onLoad } from '@dcloudio/uni-app'
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
import { getNote, createNote, updateNote, deleteNote } from '@/api/note.js'
|
import { getNote, createNote, updateNote, deleteNote } from '@/api/note.js'
|
||||||
import { formatDate } from '@/utils/date.js'
|
import { formatDate } from '@/utils/date.js'
|
||||||
|
import NoteRichEditor from '@/components/NoteRichEditor.vue'
|
||||||
|
|
||||||
const noteId = ref('')
|
const noteId = ref('')
|
||||||
const saving = ref(false)
|
const saving = ref(false)
|
||||||
const editorCtx = ref(null)
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
title: '',
|
title: '',
|
||||||
content: '',
|
content: '',
|
||||||
@@ -65,21 +62,6 @@ const meta = reactive({
|
|||||||
updatedAt: 0
|
updatedAt: 0
|
||||||
})
|
})
|
||||||
|
|
||||||
function onEditorReady() {
|
|
||||||
uni.createSelectorQuery().select('#noteEditor').context((res) => {
|
|
||||||
if (res && res.context) {
|
|
||||||
editorCtx.value = res.context
|
|
||||||
if (form.content) {
|
|
||||||
editorCtx.value.setContents({ html: form.content })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).exec()
|
|
||||||
}
|
|
||||||
|
|
||||||
function onEditorInput(e) {
|
|
||||||
form.content = e.detail.html || ''
|
|
||||||
}
|
|
||||||
|
|
||||||
onLoad(async (query) => {
|
onLoad(async (query) => {
|
||||||
if (query?.id) {
|
if (query?.id) {
|
||||||
noteId.value = query.id
|
noteId.value = query.id
|
||||||
@@ -102,9 +84,6 @@ async function loadNote(id) {
|
|||||||
form.content = note.content
|
form.content = note.content
|
||||||
form.pinned = !!note.pinned
|
form.pinned = !!note.pinned
|
||||||
meta.updatedAt = note.updatedAt
|
meta.updatedAt = note.updatedAt
|
||||||
if (editorCtx.value && note.content) {
|
|
||||||
editorCtx.value.setContents({ html: note.content })
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
uni.showToast({ title: '加载失败', icon: 'none' })
|
uni.showToast({ title: '加载失败', icon: 'none' })
|
||||||
}
|
}
|
||||||
@@ -218,13 +197,11 @@ function onDelete() {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
padding: 20rpx;
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: $color-text;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
overflow-y: auto;
|
overflow: hidden;
|
||||||
background: $color-bg-page;
|
background: #f5f7fa;
|
||||||
border-radius: $radius-md;
|
border-radius: 12rpx;
|
||||||
|
padding: 20rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.placeholder {
|
.placeholder {
|
||||||
|
|||||||
@@ -111,7 +111,20 @@ function clearSearch() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function preview(content) {
|
function preview(content) {
|
||||||
const text = (content || '').replace(/<[^>]+>/g, '').replace(/ /g, ' ').replace(/\s+/g, ' ').trim()
|
if (!content) return '暂无内容'
|
||||||
|
let html = content
|
||||||
|
.replace(/<\/t[dh]>/gi, ' ')
|
||||||
|
.replace(/<\/tr>/gi, ' ')
|
||||||
|
.replace(/<\/p>/gi, ' ')
|
||||||
|
.replace(/<br\s*\/?>/gi, ' ')
|
||||||
|
const text = html
|
||||||
|
.replace(/<[^>]+>/g, '')
|
||||||
|
.replace(/ /g, ' ')
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/\s+/g, ' ')
|
||||||
|
.trim()
|
||||||
if (!text) return '暂无内容'
|
if (!text) return '暂无内容'
|
||||||
return text.length > 60 ? `${text.slice(0, 60)}...` : text
|
return text.length > 60 ? `${text.slice(0, 60)}...` : text
|
||||||
}
|
}
|
||||||
@@ -220,6 +233,7 @@ function onDelete(item) {
|
|||||||
@include card;
|
@include card;
|
||||||
padding: 28rpx 24rpx;
|
padding: 28rpx 24rpx;
|
||||||
margin-bottom: 20rpx;
|
margin-bottom: 20rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
opacity: 0.92;
|
opacity: 0.92;
|
||||||
@@ -267,11 +281,18 @@ function onDelete(item) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.note-preview {
|
.note-preview {
|
||||||
display: block;
|
display: -webkit-box;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
font-size: 26rpx;
|
font-size: 26rpx;
|
||||||
color: $color-text-secondary;
|
color: $color-text-secondary;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
margin-bottom: 20rpx;
|
margin-bottom: 20rpx;
|
||||||
|
word-break: break-all;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.note-actions {
|
.note-actions {
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
/**
|
||||||
|
* 富文本 HTML 展示样式(与 platform UmoEditor / TipTap 输出对齐)
|
||||||
|
* 用于 editor 组件、rich-text、v-html 等场景
|
||||||
|
*/
|
||||||
|
.ql-editor,
|
||||||
|
.rich-content {
|
||||||
|
overflow-x: auto;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 8px 0;
|
||||||
|
line-height: 1.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
img,
|
||||||
|
video {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
border-left: 4px solid #3973ff;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
color: #606266;
|
||||||
|
padding: 8px 16px;
|
||||||
|
margin: 12px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
border: 1px solid #e4e7ed;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
margin: 12px 0;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
border: 1px solid #e4e7ed;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 2px 6px;
|
||||||
|
font-family: Consolas, Monaco, monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: collapse !important;
|
||||||
|
border: 1px solid #e4e7ed !important;
|
||||||
|
width: 100% !important;
|
||||||
|
margin: 12px 0;
|
||||||
|
table-layout: auto;
|
||||||
|
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
border: 1px solid #e4e7ed !important;
|
||||||
|
background-color: #ffffff !important;
|
||||||
|
color: #303133 !important;
|
||||||
|
padding: 8px 12px !important;
|
||||||
|
min-width: 60px;
|
||||||
|
vertical-align: top;
|
||||||
|
word-break: break-word;
|
||||||
|
user-select: text;
|
||||||
|
-webkit-user-select: text;
|
||||||
|
cursor: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background-color: #f5f7fa !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ul,
|
||||||
|
ol {
|
||||||
|
padding-left: 24px;
|
||||||
|
margin: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
line-height: 1.8;
|
||||||
|
margin: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid #e4e7ed;
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
/** H5 富文本表格 DOM 操作 */
|
||||||
|
|
||||||
|
export function createEmptyCell(tagName = 'td') {
|
||||||
|
const cell = document.createElement(tagName)
|
||||||
|
cell.innerHTML = '<p><br></p>'
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildTableHtml(rows = 3, cols = 3) {
|
||||||
|
let html = '<table><tbody>'
|
||||||
|
for (let r = 0; r < rows; r += 1) {
|
||||||
|
html += '<tr>'
|
||||||
|
for (let c = 0; c < cols; c += 1) {
|
||||||
|
html += '<td><p><br></p></td>'
|
||||||
|
}
|
||||||
|
html += '</tr>'
|
||||||
|
}
|
||||||
|
html += '</tbody></table><p><br></p>'
|
||||||
|
return html
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCurrentCell(editorEl) {
|
||||||
|
const sel = window.getSelection()
|
||||||
|
if (!sel || sel.rangeCount === 0) return null
|
||||||
|
let node = sel.anchorNode
|
||||||
|
if (node?.nodeType === Node.TEXT_NODE) node = node.parentElement
|
||||||
|
const cell = node?.closest?.('td, th')
|
||||||
|
if (!cell || !editorEl?.contains(cell)) return null
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addRowBefore(cell) {
|
||||||
|
const row = cell.closest('tr')
|
||||||
|
const newRow = row.cloneNode(true)
|
||||||
|
newRow.querySelectorAll('td, th').forEach((item) => {
|
||||||
|
item.innerHTML = '<p><br></p>'
|
||||||
|
item.removeAttribute('colspan')
|
||||||
|
item.removeAttribute('rowspan')
|
||||||
|
})
|
||||||
|
row.before(newRow)
|
||||||
|
return newRow.cells[cell.cellIndex] || newRow.cells[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addRowAfter(cell) {
|
||||||
|
const row = cell.closest('tr')
|
||||||
|
const newRow = row.cloneNode(true)
|
||||||
|
newRow.querySelectorAll('td, th').forEach((item) => {
|
||||||
|
item.innerHTML = '<p><br></p>'
|
||||||
|
item.removeAttribute('colspan')
|
||||||
|
item.removeAttribute('rowspan')
|
||||||
|
})
|
||||||
|
row.after(newRow)
|
||||||
|
return newRow.cells[cell.cellIndex] || newRow.cells[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteRow(cell) {
|
||||||
|
const row = cell.closest('tr')
|
||||||
|
const section = row?.parentElement
|
||||||
|
if (!row || !section || section.querySelectorAll('tr').length <= 1) return null
|
||||||
|
const focusCell =
|
||||||
|
row.nextElementSibling?.cells[cell.cellIndex] ||
|
||||||
|
row.previousElementSibling?.cells[cell.cellIndex] ||
|
||||||
|
null
|
||||||
|
row.remove()
|
||||||
|
return focusCell
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addColBefore(cell) {
|
||||||
|
const index = cell.cellIndex
|
||||||
|
const table = cell.closest('table')
|
||||||
|
table.querySelectorAll('tr').forEach((row) => {
|
||||||
|
const refCell = row.cells[index]
|
||||||
|
if (!refCell) return
|
||||||
|
refCell.before(createEmptyCell(refCell.tagName.toLowerCase()))
|
||||||
|
})
|
||||||
|
return cell.parentElement.cells[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addColAfter(cell) {
|
||||||
|
const index = cell.cellIndex
|
||||||
|
const table = cell.closest('table')
|
||||||
|
table.querySelectorAll('tr').forEach((row) => {
|
||||||
|
const refCell = row.cells[index]
|
||||||
|
if (!refCell) return
|
||||||
|
refCell.after(createEmptyCell(refCell.tagName.toLowerCase()))
|
||||||
|
})
|
||||||
|
return cell.parentElement.cells[index + 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteCol(cell) {
|
||||||
|
const index = cell.cellIndex
|
||||||
|
const table = cell.closest('table')
|
||||||
|
if (cell.parentElement.cells.length <= 1) return null
|
||||||
|
let focusCell = null
|
||||||
|
table.querySelectorAll('tr').forEach((row) => {
|
||||||
|
const target = row.cells[index]
|
||||||
|
if (!target) return
|
||||||
|
if (row === cell.parentElement) {
|
||||||
|
focusCell = row.cells[index + 1] || row.cells[index - 1] || null
|
||||||
|
}
|
||||||
|
target.remove()
|
||||||
|
})
|
||||||
|
return focusCell
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteTable(cell) {
|
||||||
|
cell.closest('table')?.remove()
|
||||||
|
}
|
||||||
+27
-9
@@ -1,16 +1,34 @@
|
|||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import uni from '@dcloudio/vite-plugin-uni'
|
import uni from '@dcloudio/vite-plugin-uni'
|
||||||
|
import path from 'node:path'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
|
||||||
export default defineConfig({
|
const rootDir = path.dirname(fileURLToPath(import.meta.url))
|
||||||
plugins: [
|
|
||||||
uni()
|
export default defineConfig({
|
||||||
],
|
plugins: [uni()],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': rootDir,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
proxy: {
|
||||||
|
// H5 开发:api/config.js 默认 baseURL 为 /proxy-api,转发到 Go 后端
|
||||||
|
'/proxy-api': {
|
||||||
|
// target: 'http://127.0.0.1:9000',
|
||||||
|
target: 'https://api.yunzer.cn',
|
||||||
|
changeOrigin: true,
|
||||||
|
rewrite: (path) => path.replace(/^\/proxy-api/, ''),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
css: {
|
css: {
|
||||||
preprocessorOptions: {
|
preprocessorOptions: {
|
||||||
scss: {
|
scss: {
|
||||||
// 关闭废弃 API 警告
|
// 关闭废弃 API 警告
|
||||||
silenceDeprecations: ['legacy-js-api', 'color-functions', 'import'],
|
silenceDeprecations: ['legacy-js-api', 'color-functions', 'import'],
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user