小程序初始提交
This commit is contained in:
129
cool-unix/pages/advice/index.uvue
Normal file
129
cool-unix/pages/advice/index.uvue
Normal file
@@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<cl-page title="工作建议">
|
||||
<view class="p-4">
|
||||
<!-- 范围选择 -->
|
||||
<!-- 行1:范围 + 周/月 -->
|
||||
<view class="flex items-center gap-2 mb-2">
|
||||
<text class="label">范围:</text>
|
||||
<view class="chip" :class="{ active: scope==='week' }" @tap="setScope('week')">周</view>
|
||||
<view class="chip" :class="{ active: scope==='month' }" @tap="setScope('month')">月</view>
|
||||
</view>
|
||||
|
||||
<!-- 行2:日期输入 + 刷新(左侧成组,右侧按钮) -->
|
||||
<view class="flex items-center justify-between mb-4">
|
||||
<view class="flex items-center gap-2 flex-1">
|
||||
<template v-if="scope === 'week'">
|
||||
<text class="text-gray-500">周起始(周一):</text>
|
||||
<view class="flex-1">
|
||||
<cl-input v-model="weekStart" type="date" placeholder="选择本周周一" />
|
||||
</view>
|
||||
</template>
|
||||
<template v-else>
|
||||
<text class="text-gray-500">月份(YYYY-MM):</text>
|
||||
<view class="flex-1">
|
||||
<cl-input v-model="month" placeholder="例如 2025-11" />
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
<cl-button type="primary" size="small" style="margin-left: 5px;" @tap="loadAdvice">刷新</cl-button>
|
||||
</view>
|
||||
|
||||
<!-- 建议内容 -->
|
||||
<view v-if="loading" class="py-12 text-center text-gray-400">加载中...</view>
|
||||
<view v-else>
|
||||
<view v-if="adviceText" class="p-4 bg-white rounded-2xl whitespace-pre-wrap">{{ adviceText }}</view>
|
||||
<view v-else class="p-8 text-center text-gray-400">{{ emptyText }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { request, useStore } from '@/cool';
|
||||
import { useUi } from '@/uni_modules/cool-ui';
|
||||
|
||||
const ui = useUi();
|
||||
const { user } = useStore();
|
||||
|
||||
const scope = ref<'week' | 'month'>('week');
|
||||
const weekStart = ref(''); // yyyy-MM-dd(周一)
|
||||
const month = ref(''); // yyyy-MM
|
||||
const adviceText = ref('');
|
||||
const emptyText = ref('暂无建议,请调整时间范围后重试');
|
||||
const loading = ref(false);
|
||||
|
||||
onMounted(async () => {
|
||||
if (user.token) { try { await user.get(); } catch {} }
|
||||
// 默认周:设置为当前周周一;默认月:当前月
|
||||
const today = new Date();
|
||||
const monday = new Date(today);
|
||||
const day = monday.getDay();
|
||||
const diff = (day === 0 ? -6 : 1 - day); // 周一为 1,周日为 0
|
||||
monday.setDate(monday.getDate() + diff);
|
||||
weekStart.value = fmtYMD(monday);
|
||||
month.value = `${monday.getFullYear()}-${String(monday.getMonth() + 1).padStart(2,'0')}`;
|
||||
loadAdvice();
|
||||
});
|
||||
|
||||
function setScope(s: 'week' | 'month') {
|
||||
scope.value = s;
|
||||
}
|
||||
|
||||
function fmtYMD(d: Date) {
|
||||
const Y = d.getFullYear();
|
||||
const M = String(d.getMonth() + 1).padStart(2, '0');
|
||||
const D = String(d.getDate()).padStart(2, '0');
|
||||
return `${Y}-${M}-${D}`;
|
||||
}
|
||||
|
||||
async function loadAdvice() {
|
||||
try {
|
||||
loading.value = true;
|
||||
adviceText.value = '';
|
||||
if (!user.info.value?.id) {
|
||||
return ui.showToast({ message: '请先登录', type: 'error' });
|
||||
}
|
||||
const params: any = { scope: scope.value, userId: user.info.value.id };
|
||||
if (scope.value === 'week') {
|
||||
if (!weekStart.value) return ui.showToast({ message: '请选择周一日期', type: 'error' });
|
||||
params.startDate = weekStart.value;
|
||||
} else {
|
||||
const m = (month.value || '').trim();
|
||||
if (!/^\d{4}-\d{2}$/.test(m)) return ui.showToast({ message: '请输入有效月份,如 2025-11', type: 'error' });
|
||||
params.startDate = `${m}-01`;
|
||||
}
|
||||
const res = await request({ url: '/app/useradvice/advice', method: 'GET', params });
|
||||
adviceText.value = res?.adviceText || '';
|
||||
if (!adviceText.value) {
|
||||
emptyText.value = scope.value === 'month'
|
||||
? '未查询到该月份的月报/周报/日报内容,请核实后再尝试'
|
||||
: '未查询到该周的可用内容,请核实后再尝试';
|
||||
}
|
||||
} catch (e: any) {
|
||||
adviceText.value = '';
|
||||
emptyText.value = e?.message || (scope.value === 'month'
|
||||
? '未查询到该月份的月报/周报/日报内容,请核实后再尝试'
|
||||
: '未查询到该周的可用内容,请核实后再尝试');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.label { color: #6b7280; /* gray-500 */ margin-right: 8px; }
|
||||
.chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 4px 12px;
|
||||
border-radius: 9999px;
|
||||
border: 1px solid #d1d5db; /* gray-300 */
|
||||
color: #374151; /* gray-700 */
|
||||
}
|
||||
.chip.active {
|
||||
background: #3b82f6; /* blue-500 */
|
||||
border-color: #3b82f6;
|
||||
color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
222
cool-unix/pages/dailyreport/detail.uvue
Normal file
222
cool-unix/pages/dailyreport/detail.uvue
Normal file
@@ -0,0 +1,222 @@
|
||||
<template>
|
||||
<cl-page title="日报详情">
|
||||
<view v-if="loading" class="flex justify-center items-center py-20">
|
||||
<cl-loading />
|
||||
</view>
|
||||
|
||||
<view v-else-if="report" class="p-4">
|
||||
<!-- 头部信息 -->
|
||||
<view class="mb-6">
|
||||
<view class="flex justify-between items-center mb-2">
|
||||
<text class="text-2xl font-bold">{{ formatDate(report.reportDate) }}</text>
|
||||
<cl-tag :type="report.status === 1 ? 'success' : 'warning'">
|
||||
{{ report.status === 1 ? '已提交' : '草稿' }}
|
||||
</cl-tag>
|
||||
</view>
|
||||
<text class="text-sm text-gray-500">
|
||||
{{ report.inputType === 1 ? "🎤 语音输入" : "⌨️ 文字输入" }} ·
|
||||
{{ report.submitTime ? ('提交于 ' + formatDateTime(report.submitTime)) : ('创建于 ' + formatDateTime(report.createTime)) }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 最终日报内容 -->
|
||||
<view class="mb-6">
|
||||
<text class="text-base font-bold mb-3 block">📝 日报内容</text>
|
||||
<view class="p-4 bg-white rounded-lg shadow-sm">
|
||||
<text class="text-sm text-gray-800">{{ report.userEditedContent || "暂无内容" }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 折叠面板:查看原始内容和AI生成内容 -->
|
||||
<cl-collapse v-model="activeNames">
|
||||
<!-- 原始内容 -->
|
||||
<cl-collapse-item v-if="report.originalText" name="original" title="📄 原始输入内容">
|
||||
<view class="p-3 bg-gray-50 rounded">
|
||||
<text class="text-sm text-gray-700">{{ report.originalText }}</text>
|
||||
</view>
|
||||
</cl-collapse-item>
|
||||
|
||||
<!-- AI格式化内容 -->
|
||||
<cl-collapse-item v-if="report.aiFormattedContent" name="ai" title="🤖 AI生成内容">
|
||||
<view class="p-3 bg-blue-50 rounded">
|
||||
<text class="text-sm text-gray-800">{{ report.aiFormattedContent }}</text>
|
||||
</view>
|
||||
</cl-collapse-item>
|
||||
</cl-collapse>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="flex gap-3 mt-6">
|
||||
<cl-button type="primary" size="large" :flex="1" @tap="editReport">
|
||||
编辑日报
|
||||
</cl-button>
|
||||
<cl-button v-if="report.status === 0" type="success" size="large" :flex="1" @tap="submitReport" :loading="isSubmitting">
|
||||
{{ isSubmitting ? "提交中..." : "提交日报" }}
|
||||
</cl-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-else class="flex flex-col items-center justify-center py-20">
|
||||
<text class="text-6xl mb-4">📝</text>
|
||||
<text class="text-gray-400 text-base">日报不存在</text>
|
||||
<cl-button type="primary" size="small" class="mt-4" @tap="router.back()">
|
||||
返回列表
|
||||
</cl-button>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { request, router, useStore } from "@/cool";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
|
||||
const ui = useUi();
|
||||
const { user } = useStore();
|
||||
|
||||
// 日报数据
|
||||
const report = ref<any>(null);
|
||||
const loading = ref(true);
|
||||
const isSubmitting = ref(false);
|
||||
|
||||
// 折叠面板展开项
|
||||
const activeNames = ref<string[]>([]);
|
||||
|
||||
// 用户ID(从登录状态获取)
|
||||
const userId = ref(0);
|
||||
|
||||
// 路由参数(onLoad 中获取)
|
||||
const reportId = ref("");
|
||||
const reportDateParam = ref("");
|
||||
|
||||
onLoad(async (options: any) => {
|
||||
// 读取路由参数
|
||||
reportId.value = (options?.id ?? "").toString();
|
||||
reportDateParam.value = (options?.date ?? "").toString();
|
||||
// 刷新用户信息
|
||||
if (user.token) {
|
||||
try { await user.get(); } catch (e) { console.error("【日报详情】获取用户信息失败:", e); }
|
||||
}
|
||||
|
||||
// 获取当前登录用户ID
|
||||
if (user.info.value && user.info.value.id) {
|
||||
userId.value = user.info.value.id;
|
||||
} else {
|
||||
ui.showToast({ message: "请先登录", type: "error" });
|
||||
setTimeout(() => { router.to("/pages/user/login"); }, 1000);
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (reportId.value) {
|
||||
await loadReportDetail();
|
||||
} else if (reportDateParam.value) {
|
||||
await loadReportDetailByDateFallback();
|
||||
} else {
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
// 加载日报详情(按ID)
|
||||
async function loadReportDetail() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await request({
|
||||
url: "/app/dailyreport/report/detail",
|
||||
method: "GET",
|
||||
params: { id: reportId.value, userId: userId.value }
|
||||
});
|
||||
if (res) { report.value = res; return; }
|
||||
await loadReportDetailByDateFallback();
|
||||
} catch (error: any) {
|
||||
console.error("加载日报详情失败:", error);
|
||||
await loadReportDetailByDateFallback();
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 按日期回退查询
|
||||
async function loadReportDetailByDateFallback() {
|
||||
if (!reportDateParam.value) return;
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await request({
|
||||
url: "/app/dailyreport/report/myReports",
|
||||
method: "GET",
|
||||
params: {
|
||||
userId: userId.value,
|
||||
startDate: reportDateParam.value,
|
||||
endDate: reportDateParam.value
|
||||
}
|
||||
});
|
||||
if (Array.isArray(res) && res.length > 0) {
|
||||
report.value = res[0];
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error("按日期加载日报失败:", error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 编辑日报
|
||||
function editReport() {
|
||||
const id = reportId.value || report.value?.id;
|
||||
if (id) {
|
||||
router.to(`/pages/dailyreport/submit?id=${id}`);
|
||||
} else if (reportDateParam.value) {
|
||||
router.to(`/pages/dailyreport/submit?date=${reportDateParam.value}`);
|
||||
} else {
|
||||
ui.showToast({ message: "无有效日报标识", type: "warn" });
|
||||
}
|
||||
}
|
||||
|
||||
// 提交日报(草稿转提交)
|
||||
async function submitReport() {
|
||||
if (!report.value) return;
|
||||
isSubmitting.value = true;
|
||||
try {
|
||||
await request({
|
||||
url: "/app/dailyreport/report/submit",
|
||||
method: "POST",
|
||||
data: {
|
||||
userId: userId.value,
|
||||
reportDate: report.value.reportDate,
|
||||
originalText: report.value.originalText,
|
||||
aiFormattedContent: report.value.aiFormattedContent,
|
||||
userEditedContent: report.value.userEditedContent,
|
||||
inputType: report.value.inputType
|
||||
}
|
||||
});
|
||||
ui.showToast({ message: "日报提交成功", type: "success" });
|
||||
setTimeout(() => { loadReportDetail(); }, 500);
|
||||
} catch (error: any) {
|
||||
console.error("提交日报失败:", error);
|
||||
ui.showToast({ message: "提交失败: " + (error.message || "未知错误"), type: "error" });
|
||||
} finally {
|
||||
isSubmitting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 工具:日期与时间格式化
|
||||
function formatDate(dateStr: string) {
|
||||
if (!dateStr) return "";
|
||||
const d = new Date(dateStr);
|
||||
const y = d.getFullYear();
|
||||
const m = String(d.getMonth() + 1).padStart(2, "0");
|
||||
const dd = String(d.getDate()).padStart(2, "0");
|
||||
return `${y}-${m}-${dd}`;
|
||||
}
|
||||
|
||||
function formatDateTime(timeStr: string) {
|
||||
if (!timeStr) return "";
|
||||
const date = new Date(timeStr);
|
||||
const y = date.getFullYear();
|
||||
const m = String(date.getMonth() + 1).padStart(2, "0");
|
||||
const d = String(date.getDate()).padStart(2, "0");
|
||||
const hh = String(date.getHours()).padStart(2, "0");
|
||||
const mm = String(date.getMinutes()).padStart(2, "0");
|
||||
return `${y}-${m}-${d} ${hh}:${mm}`;
|
||||
}
|
||||
</script>
|
||||
327
cool-unix/pages/dailyreport/list.uvue
Normal file
327
cool-unix/pages/dailyreport/list.uvue
Normal file
@@ -0,0 +1,327 @@
|
||||
<template>
|
||||
<cl-page title="我的日报">
|
||||
<!-- 筛选栏 -->
|
||||
<view class="px-4 py-3 bg-white border-b">
|
||||
<view class="flex gap-2">
|
||||
<cl-button
|
||||
:type="filterType === 'all' ? 'primary' : 'default'"
|
||||
size="small"
|
||||
@tap="changeFilter('all')"
|
||||
>
|
||||
全部
|
||||
</cl-button>
|
||||
<cl-button
|
||||
:type="filterType === 'draft' ? 'primary' : 'default'"
|
||||
size="small"
|
||||
@tap="changeFilter('draft')"
|
||||
>
|
||||
草稿
|
||||
</cl-button>
|
||||
<cl-button
|
||||
:type="filterType === 'submitted' ? 'primary' : 'default'"
|
||||
size="small"
|
||||
@tap="changeFilter('submitted')"
|
||||
>
|
||||
已提交
|
||||
</cl-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 日报列表 -->
|
||||
<scroll-view scroll-y class="flex-1">
|
||||
<!-- 加载中 -->
|
||||
<view v-if="loading && list.length === 0" class="flex items-center justify-center py-20">
|
||||
<text class="text-gray-400">加载中...</text>
|
||||
</view>
|
||||
|
||||
<!-- 列表内容 -->
|
||||
<view v-else-if="list.length > 0">
|
||||
<view
|
||||
v-for="(item, index) in list"
|
||||
:key="item.id || index"
|
||||
class="mx-4 my-2 p-4 bg-white rounded-lg shadow-sm"
|
||||
@tap="toDetail(item)"
|
||||
>
|
||||
<!-- 头部:日期和状态 -->
|
||||
<view class="flex justify-between items-center mb-3">
|
||||
<view class="flex items-center gap-2">
|
||||
<text class="text-lg font-bold">{{ formatDate(item.reportDate) }}</text>
|
||||
<text class="text-xs text-gray-500">
|
||||
{{ formatWeekday(item.reportDate) }}
|
||||
</text>
|
||||
</view>
|
||||
<view
|
||||
:class="[
|
||||
'px-2 py-1 rounded text-xs',
|
||||
item.status === 1 ? 'bg-green-100 text-green-700' : 'bg-yellow-100 text-yellow-700'
|
||||
]"
|
||||
>
|
||||
{{ item.status === 1 ? "已提交" : "草稿" }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 内容摘要 -->
|
||||
<view class="mb-3">
|
||||
<text class="text-sm text-gray-700">
|
||||
{{ getContentPreview(item.userEditedContent) }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 底部信息 -->
|
||||
<view class="flex justify-between items-center text-xs text-gray-500">
|
||||
<view class="flex items-center gap-2">
|
||||
<text>{{ item.inputType === 1 ? "🎤 语音输入" : "⌨️ 文字输入" }}</text>
|
||||
</view>
|
||||
<text v-if="item.submitTime">
|
||||
提交于 {{ formatTime(item.submitTime) }}
|
||||
</text>
|
||||
<text v-else-if="item.createTime">
|
||||
创建于 {{ formatTime(item.createTime) }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载更多提示 -->
|
||||
<view v-if="loading" class="flex items-center justify-center py-4">
|
||||
<text class="text-gray-400 text-sm">加载中...</text>
|
||||
</view>
|
||||
<view v-else-if="finished" class="flex items-center justify-center py-4">
|
||||
<text class="text-gray-400 text-sm">没有更多了</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view v-else class="flex flex-col items-center justify-center py-20">
|
||||
<text class="text-6xl mb-4">📝</text>
|
||||
<text class="text-gray-400 text-base">暂无日报</text>
|
||||
<view
|
||||
class="mt-4 px-4 py-2 bg-blue-500 text-white rounded"
|
||||
@tap="toSubmit"
|
||||
>
|
||||
<text>去提交日报</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 悬浮提交按钮 -->
|
||||
<view class="fixed bottom-20 right-4">
|
||||
<cl-button
|
||||
type="primary"
|
||||
size="large"
|
||||
round
|
||||
@tap="toSubmit"
|
||||
>
|
||||
<text class="text-2xl">✏️</text>
|
||||
</cl-button>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from "vue";
|
||||
import { request, router, useStore } from "@/cool";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
|
||||
const ui = useUi();
|
||||
const { user } = useStore();
|
||||
|
||||
// 列表相关
|
||||
const list = ref<any[]>([]);
|
||||
const loading = ref(false);
|
||||
const finished = ref(false);
|
||||
const page = ref(1);
|
||||
const pageSize = ref(20);
|
||||
|
||||
// 筛选类型
|
||||
const filterType = ref<"all" | "draft" | "submitted">("all");
|
||||
|
||||
// 用户ID(从登录状态获取)
|
||||
const userId = ref(0);
|
||||
|
||||
// 初始化
|
||||
onMounted(async () => {
|
||||
console.log("【日报列表】页面加载, user.info:", user.info.value);
|
||||
|
||||
// 先尝试获取最新用户信息
|
||||
if (user.token) {
|
||||
try {
|
||||
await user.get();
|
||||
} catch (e) {
|
||||
console.error("【日报列表】获取用户信息失败:", e);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("【日报列表】获取用户信息后, user.info:", user.info.value);
|
||||
|
||||
if (user.info.value && user.info.value.id) {
|
||||
userId.value = user.info.value.id;
|
||||
console.log("【日报列表】设置userId:", userId.value);
|
||||
loadReports();
|
||||
} else {
|
||||
console.error("【日报列表】用户未登录或用户信息为空");
|
||||
ui.showToast({
|
||||
message: "请先登录",
|
||||
type: "error"
|
||||
});
|
||||
setTimeout(() => {
|
||||
router.to("/pages/user/login");
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
|
||||
// 加载日报列表
|
||||
async function loadReports() {
|
||||
if (loading.value || finished.value) return;
|
||||
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
console.log("【日报列表】开始加载, userId:", userId.value);
|
||||
|
||||
if (!userId.value || userId.value === 0) {
|
||||
console.error("【日报列表】userId无效:", userId.value);
|
||||
throw new Error("用户ID无效,请重新登录");
|
||||
}
|
||||
|
||||
const params: any = {
|
||||
userId: userId.value,
|
||||
page: page.value,
|
||||
size: pageSize.value
|
||||
};
|
||||
|
||||
// 添加状态筛选
|
||||
if (filterType.value === "draft") {
|
||||
params.status = 0;
|
||||
} else if (filterType.value === "submitted") {
|
||||
params.status = 1;
|
||||
}
|
||||
|
||||
console.log("【日报列表】请求参数:", params);
|
||||
|
||||
const res = await request({
|
||||
url: "/app/dailyreport/report/myReports",
|
||||
method: "GET",
|
||||
params
|
||||
});
|
||||
|
||||
console.log("【日报列表】响应数据:", res);
|
||||
console.log("【日报列表】响应类型:", typeof res);
|
||||
console.log("【日报列表】是否为数组:", Array.isArray(res));
|
||||
console.log("【日报列表】数据长度:", res ? res.length : 0);
|
||||
|
||||
if (res && res.length > 0) {
|
||||
console.log("【日报列表】开始填充列表数据");
|
||||
if (page.value === 1) {
|
||||
list.value = res;
|
||||
} else {
|
||||
list.value.push(...res);
|
||||
}
|
||||
page.value++;
|
||||
|
||||
console.log("【日报列表】列表已更新, 当前list长度:", list.value.length);
|
||||
console.log("【日报列表】列表第一项数据:", list.value[0]);
|
||||
|
||||
// 如果返回数据少于每页大小,说明已经到底了
|
||||
if (res.length < pageSize.value) {
|
||||
finished.value = true;
|
||||
}
|
||||
} else {
|
||||
console.log("【日报列表】无数据或数据为空");
|
||||
// 即使没有数据,也不应该标记为finished,可能只是筛选条件没有匹配的数据
|
||||
if (page.value === 1) {
|
||||
list.value = [];
|
||||
}
|
||||
finished.value = true;
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error("加载日报列表失败:", error);
|
||||
ui.showToast({
|
||||
message: "加载失败: " + (error.message || "未知错误"),
|
||||
type: "error"
|
||||
});
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 下拉刷新
|
||||
function onRefresh() {
|
||||
console.log("【日报列表】下拉刷新");
|
||||
page.value = 1;
|
||||
finished.value = false;
|
||||
list.value = [];
|
||||
loadReports();
|
||||
}
|
||||
|
||||
// 切换筛选条件
|
||||
function changeFilter(type: "all" | "draft" | "submitted") {
|
||||
console.log("【日报列表】切换筛选:", type);
|
||||
filterType.value = type;
|
||||
page.value = 1;
|
||||
finished.value = false;
|
||||
list.value = [];
|
||||
loadReports();
|
||||
}
|
||||
|
||||
// 跳转到详情
|
||||
function toDetail(item: any) {
|
||||
const date = item.reportDate ? encodeURIComponent(item.reportDate) : '';
|
||||
router.to(`/pages/dailyreport/detail?id=${item.id}&date=${date}`);
|
||||
}
|
||||
|
||||
// 跳转到提交页面
|
||||
function toSubmit() {
|
||||
router.to("/pages/dailyreport/submit");
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
function formatDate(dateStr: string) {
|
||||
if (!dateStr) return "";
|
||||
const date = new Date(dateStr);
|
||||
const month = String(date.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(date.getDate()).padStart(2, "0");
|
||||
return `${month}月${day}日`;
|
||||
}
|
||||
|
||||
// 格式化星期
|
||||
function formatWeekday(dateStr: string) {
|
||||
if (!dateStr) return "";
|
||||
const date = new Date(dateStr);
|
||||
const weekdays = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"];
|
||||
return weekdays[date.getDay()];
|
||||
}
|
||||
|
||||
// 格式化时间
|
||||
function formatTime(timeStr: string) {
|
||||
if (!timeStr) return "";
|
||||
const date = new Date(timeStr);
|
||||
const month = String(date.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(date.getDate()).padStart(2, "0");
|
||||
const hours = String(date.getHours()).padStart(2, "0");
|
||||
const minutes = String(date.getMinutes()).padStart(2, "0");
|
||||
return `${month}-${day} ${hours}:${minutes}`;
|
||||
}
|
||||
|
||||
// 获取内容摘要
|
||||
function getContentPreview(content: string) {
|
||||
if (!content) return "(无内容)";
|
||||
// 移除 Markdown 标记
|
||||
const plain = content.replace(/[#*`\[\]()]/g, "").trim();
|
||||
// 限制长度
|
||||
return plain.length > 100 ? plain.substring(0, 100) + "..." : plain;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.gap-2 {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.line-clamp-3 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
||||
555
cool-unix/pages/dailyreport/submit.uvue
Normal file
555
cool-unix/pages/dailyreport/submit.uvue
Normal file
@@ -0,0 +1,555 @@
|
||||
<template>
|
||||
<cl-page title="提交日报">
|
||||
<view class="p-4">
|
||||
<!-- 日期选择 -->
|
||||
<view class="mb-4">
|
||||
<text class="text-base font-bold mb-2">日报日期</text>
|
||||
<cl-input
|
||||
v-model="form.reportDate"
|
||||
type="date"
|
||||
placeholder="选择日报日期"
|
||||
:max="todayDate"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 输入方式选择 -->
|
||||
<view class="mb-4">
|
||||
<text class="text-base font-bold mb-2">输入方式</text>
|
||||
<view class="flex gap-2">
|
||||
<cl-button
|
||||
:type="inputType === 0 ? 'primary' : 'default'"
|
||||
size="small"
|
||||
@tap="inputType = 0"
|
||||
>
|
||||
文字输入
|
||||
</cl-button>
|
||||
<cl-button
|
||||
:type="inputType === 1 ? 'primary' : 'default'"
|
||||
size="small"
|
||||
@tap="inputType = 1"
|
||||
>
|
||||
语音输入
|
||||
</cl-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 语音输入区域 -->
|
||||
<view v-if="inputType === 1" class="mb-4">
|
||||
<text class="text-base font-bold mb-2">语音录制</text>
|
||||
<view class="flex flex-col items-center p-6 bg-gray-50 rounded-lg">
|
||||
<view v-if="!isRecording && !audioFile" class="flex flex-col items-center">
|
||||
<text class="text-gray-500 mb-4">按住按钮开始录音(最长60秒)</text>
|
||||
<cl-button
|
||||
type="primary"
|
||||
size="large"
|
||||
round
|
||||
@touchstart="startRecording"
|
||||
@touchend="stopRecording"
|
||||
>
|
||||
<text class="text-2xl">🎤</text>
|
||||
</cl-button>
|
||||
</view>
|
||||
|
||||
<view v-if="isRecording" class="flex flex-col items-center">
|
||||
<text class="text-red-500 text-lg mb-2">录音中...</text>
|
||||
<text class="text-3xl font-bold text-red-500 mb-4">{{ recordingTime }}s</text>
|
||||
<cl-button type="danger" @tap="cancelRecording">取消录音</cl-button>
|
||||
</view>
|
||||
|
||||
<view v-if="audioFile && !isRecording" class="flex flex-col items-center w-full">
|
||||
<text class="text-green-500 mb-2">录音完成</text>
|
||||
<text class="text-gray-600 mb-4">时长: {{ recordingTime }}秒</text>
|
||||
<view class="flex gap-2">
|
||||
<cl-button type="default" @tap="reRecord">重新录音</cl-button>
|
||||
<cl-button type="primary" @tap="recognizeVoice" :loading="isRecognizing">
|
||||
{{ isRecognizing ? '识别中...' : '语音识别' }}
|
||||
</cl-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 识别结果 -->
|
||||
<view v-if="recognizedText" class="mt-4">
|
||||
<text class="text-base font-bold mb-2">识别结果</text>
|
||||
<view class="p-3 bg-blue-50 rounded-lg">
|
||||
<text class="text-gray-700">{{ recognizedText }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 文字输入区域 -->
|
||||
<view v-if="inputType === 0" class="mb-4">
|
||||
<view class="p-4 bg-white rounded-2xl">
|
||||
<cl-form>
|
||||
<cl-form-item label="工作内容" required>
|
||||
<cl-input
|
||||
v-model="originalText"
|
||||
placeholder="请输入今天的工作内容"
|
||||
:maxlength="1000"
|
||||
/>
|
||||
</cl-form-item>
|
||||
</cl-form>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- AI格式化按钮 -->
|
||||
<view v-if="originalText || recognizedText" class="mb-4">
|
||||
<cl-button
|
||||
type="success"
|
||||
size="large"
|
||||
block
|
||||
@tap="formatWithAI"
|
||||
:loading="isFormatting"
|
||||
>
|
||||
{{ isFormatting ? 'AI生成中...' : '🤖 AI生成日报' }}
|
||||
</cl-button>
|
||||
</view>
|
||||
|
||||
<!-- AI生成的日报内容 -->
|
||||
<view v-if="aiFormattedContent" class="mb-4">
|
||||
<text class="text-base font-bold mb-2">AI生成的日报</text>
|
||||
<view class="p-3 bg-green-50 rounded-lg mb-2">
|
||||
<cl-markdown :content="aiFormattedContent" />
|
||||
</view>
|
||||
<text class="text-sm text-gray-500">您可以在下方编辑最终内容</text>
|
||||
</view>
|
||||
|
||||
<!-- 最终编辑区域 -->
|
||||
<view v-if="aiFormattedContent" class="mb-4">
|
||||
<view class="p-4 bg-white rounded-2xl">
|
||||
<cl-form>
|
||||
<cl-form-item label="最终日报内容" required>
|
||||
<cl-input
|
||||
v-model="userEditedContent"
|
||||
placeholder="请编辑最终的日报内容"
|
||||
:maxlength="2000"
|
||||
/>
|
||||
</cl-form-item>
|
||||
</cl-form>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view v-if="userEditedContent" class="flex gap-2 mt-6">
|
||||
<cl-button
|
||||
type="default"
|
||||
size="large"
|
||||
:flex="1"
|
||||
@tap="saveDraft"
|
||||
:loading="isSavingDraft"
|
||||
>
|
||||
{{ isSavingDraft ? '保存中...' : '保存草稿' }}
|
||||
</cl-button>
|
||||
<cl-button
|
||||
type="primary"
|
||||
size="large"
|
||||
:flex="1"
|
||||
@tap="submitReport"
|
||||
:loading="isSubmitting"
|
||||
>
|
||||
{{ isSubmitting ? '提交中...' : '提交日报' }}
|
||||
</cl-button>
|
||||
</view>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted } from "vue";
|
||||
import { request, router, useStore } from "@/cool";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
|
||||
const ui = useUi();
|
||||
const { user } = useStore();
|
||||
|
||||
// 表单数据
|
||||
const form = ref({
|
||||
reportDate: "",
|
||||
userId: 0 // 将在onMounted中从登录用户信息获取
|
||||
});
|
||||
|
||||
// 输入方式:0-文字,1-语音
|
||||
const inputType = ref<number>(0);
|
||||
|
||||
// 语音录制相关
|
||||
const isRecording = ref(false);
|
||||
const recordingTime = ref(0);
|
||||
const audioFile = ref<string | null>(null);
|
||||
const recorderManager = ref<any>(null);
|
||||
let recordTimer: number | null = null;
|
||||
|
||||
// 识别和格式化相关
|
||||
const isRecognizing = ref(false);
|
||||
const recognizedText = ref("");
|
||||
const originalText = ref("");
|
||||
const isFormatting = ref(false);
|
||||
const aiFormattedContent = ref("");
|
||||
const userEditedContent = ref("");
|
||||
|
||||
// 提交相关
|
||||
const isSavingDraft = ref(false);
|
||||
const isSubmitting = ref(false);
|
||||
|
||||
// 今天的日期
|
||||
const todayDate = computed(() => {
|
||||
const today = new Date();
|
||||
return `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, "0")}-${String(today.getDate()).padStart(2, "0")}`;
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
console.log("【日报提交】页面加载, user.info:", user.info.value);
|
||||
|
||||
// 先尝试获取最新用户信息
|
||||
if (user.token) {
|
||||
try {
|
||||
await user.get();
|
||||
} catch (e) {
|
||||
console.error("【日报提交】获取用户信息失败:", e);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("【日报提交】获取用户信息后, user.info:", user.info.value);
|
||||
|
||||
// 获取当前登录用户ID
|
||||
if (user.info.value && user.info.value.id) {
|
||||
form.value.userId = user.info.value.id;
|
||||
console.log("【日报提交】设置userId:", form.value.userId);
|
||||
} else {
|
||||
// 如果未登录,跳转到登录页
|
||||
console.error("【日报提交】用户未登录或用户信息为空");
|
||||
ui.showToast({
|
||||
message: "请先登录",
|
||||
type: "error"
|
||||
});
|
||||
setTimeout(() => {
|
||||
router.to("/pages/user/login");
|
||||
}, 1000);
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置默认日期为今天
|
||||
form.value.reportDate = todayDate.value;
|
||||
|
||||
// 初始化录音管理器(仅在支持的平台)
|
||||
try {
|
||||
// @ts-ignore
|
||||
const manager = uni.getRecorderManager();
|
||||
|
||||
if (manager) {
|
||||
recorderManager.value = manager;
|
||||
|
||||
// 监听录音结束
|
||||
recorderManager.value.onStop((res: any) => {
|
||||
if (recordTimer) {
|
||||
clearInterval(recordTimer);
|
||||
recordTimer = null;
|
||||
}
|
||||
audioFile.value = res.tempFilePath;
|
||||
isRecording.value = false;
|
||||
});
|
||||
|
||||
// 监听录音错误
|
||||
recorderManager.value.onError((err: any) => {
|
||||
console.error("录音错误:", err);
|
||||
ui.showToast({
|
||||
message: "录音失败: " + err.errMsg,
|
||||
type: "error"
|
||||
});
|
||||
isRecording.value = false;
|
||||
if (recordTimer) {
|
||||
clearInterval(recordTimer);
|
||||
recordTimer = null;
|
||||
}
|
||||
});
|
||||
|
||||
console.log("录音管理器初始化成功");
|
||||
} else {
|
||||
console.warn("当前环境不支持录音功能(H5环境),请使用文字输入");
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("录音管理器初始化失败:", e);
|
||||
}
|
||||
|
||||
// 检查今天是否已有日报
|
||||
checkTodayReport();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (recordTimer) {
|
||||
clearInterval(recordTimer);
|
||||
}
|
||||
});
|
||||
|
||||
// 检查今天是否已有日报
|
||||
async function checkTodayReport() {
|
||||
try {
|
||||
const res = await request({
|
||||
url: "/app/dailyreport/report/todayReport",
|
||||
method: "GET",
|
||||
params: { userId: form.value.userId }
|
||||
});
|
||||
|
||||
if (res && res.id) {
|
||||
// 今天已有日报,询问是否继续编辑
|
||||
uni.showModal({
|
||||
title: "提示",
|
||||
content: `您今天已经${res.status === 1 ? "提交" : "保存"}了日报,是否继续编辑?`,
|
||||
success: (modalRes) => {
|
||||
if (modalRes.confirm) {
|
||||
// 加载已有日报
|
||||
loadExistingReport(res);
|
||||
} else {
|
||||
// 跳转到列表页
|
||||
router.back();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("今天还没有日报,可以新建");
|
||||
}
|
||||
}
|
||||
|
||||
// 加载已有日报
|
||||
function loadExistingReport(report: any) {
|
||||
originalText.value = report.originalText || "";
|
||||
aiFormattedContent.value = report.aiFormattedContent || "";
|
||||
userEditedContent.value = report.userEditedContent || "";
|
||||
inputType.value = report.inputType || 0;
|
||||
}
|
||||
|
||||
// 开始录音
|
||||
function startRecording() {
|
||||
// 检查录音管理器是否可用
|
||||
if (!recorderManager.value) {
|
||||
return ui.showToast({
|
||||
message: "当前环境不支持录音功能,请使用文字输入",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
|
||||
isRecording.value = true;
|
||||
recordingTime.value = 0;
|
||||
audioFile.value = null;
|
||||
recognizedText.value = "";
|
||||
|
||||
// 开始录音
|
||||
recorderManager.value.start({
|
||||
duration: 60000, // 最长60秒
|
||||
format: "mp3"
|
||||
});
|
||||
|
||||
// 开始计时
|
||||
recordTimer = setInterval(() => {
|
||||
recordingTime.value++;
|
||||
if (recordingTime.value >= 60) {
|
||||
stopRecording();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// 停止录音
|
||||
function stopRecording() {
|
||||
if (isRecording.value && recorderManager.value) {
|
||||
recorderManager.value.stop();
|
||||
}
|
||||
}
|
||||
|
||||
// 取消录音
|
||||
function cancelRecording() {
|
||||
if (isRecording.value && recorderManager.value) {
|
||||
recorderManager.value.stop();
|
||||
audioFile.value = null;
|
||||
recordingTime.value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 重新录音
|
||||
function reRecord() {
|
||||
audioFile.value = null;
|
||||
recognizedText.value = "";
|
||||
recordingTime.value = 0;
|
||||
}
|
||||
|
||||
// 语音识别
|
||||
async function recognizeVoice() {
|
||||
if (!audioFile.value) {
|
||||
return ui.showToast({
|
||||
message: "请先录音",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
|
||||
isRecognizing.value = true;
|
||||
|
||||
try {
|
||||
// 上传音频文件
|
||||
const uploadRes = await uni.uploadFile({
|
||||
url: request.options.baseURL + "/app/dailyreport/report/uploadVoice",
|
||||
filePath: audioFile.value,
|
||||
name: "audio",
|
||||
formData: {
|
||||
userId: form.value.userId
|
||||
}
|
||||
});
|
||||
|
||||
const result = JSON.parse(uploadRes.data);
|
||||
if (result.code === 1000) {
|
||||
recognizedText.value = result.data;
|
||||
originalText.value = result.data;
|
||||
ui.showToast({
|
||||
message: "识别成功",
|
||||
type: "success"
|
||||
});
|
||||
} else {
|
||||
throw new Error(result.message || "识别失败");
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error("语音识别失败:", error);
|
||||
ui.showToast({
|
||||
message: "语音识别失败: " + (error.message || "未知错误"),
|
||||
type: "error"
|
||||
});
|
||||
} finally {
|
||||
isRecognizing.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// AI格式化
|
||||
async function formatWithAI() {
|
||||
const text = originalText.value || recognizedText.value;
|
||||
if (!text) {
|
||||
return ui.showToast({
|
||||
message: "请先输入内容或录音",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
|
||||
isFormatting.value = true;
|
||||
|
||||
try {
|
||||
const res = await request({
|
||||
url: "/app/dailyreport/report/aiFormat",
|
||||
method: "POST",
|
||||
data: {
|
||||
originalText: text,
|
||||
reportDate: form.value.reportDate // 传递日报日期
|
||||
}
|
||||
});
|
||||
|
||||
// 后端返回的是对象 {formattedContent: "内容", length: 数字}
|
||||
const formattedContent = res.formattedContent || res;
|
||||
aiFormattedContent.value = formattedContent;
|
||||
userEditedContent.value = formattedContent;
|
||||
|
||||
ui.showToast({
|
||||
message: "AI生成成功",
|
||||
type: "success"
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.error("AI格式化失败:", error);
|
||||
ui.showToast({
|
||||
message: "AI格式化失败: " + (error.message || "未知错误"),
|
||||
type: "error"
|
||||
});
|
||||
} finally {
|
||||
isFormatting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 保存草稿
|
||||
async function saveDraft() {
|
||||
if (!userEditedContent.value) {
|
||||
return ui.showToast({
|
||||
message: "请先生成日报内容",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
|
||||
isSavingDraft.value = true;
|
||||
|
||||
try {
|
||||
await request({
|
||||
url: "/app/dailyreport/report/saveDraft",
|
||||
method: "POST",
|
||||
data: {
|
||||
userId: form.value.userId,
|
||||
reportDate: form.value.reportDate,
|
||||
originalText: originalText.value || recognizedText.value,
|
||||
aiFormattedContent: aiFormattedContent.value,
|
||||
userEditedContent: userEditedContent.value,
|
||||
inputType: inputType.value
|
||||
}
|
||||
});
|
||||
|
||||
ui.showToast({
|
||||
message: "草稿保存成功",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
// 延迟跳转
|
||||
setTimeout(() => {
|
||||
router.back();
|
||||
}, 1000);
|
||||
} catch (error: any) {
|
||||
console.error("保存草稿失败:", error);
|
||||
ui.showToast({
|
||||
message: "保存草稿失败: " + (error.message || "未知错误"),
|
||||
type: "error"
|
||||
});
|
||||
} finally {
|
||||
isSavingDraft.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 提交日报
|
||||
async function submitReport() {
|
||||
if (!userEditedContent.value) {
|
||||
return ui.showToast({
|
||||
message: "请先生成日报内容",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
|
||||
isSubmitting.value = true;
|
||||
|
||||
try {
|
||||
await request({
|
||||
url: "/app/dailyreport/report/submit",
|
||||
method: "POST",
|
||||
data: {
|
||||
userId: form.value.userId,
|
||||
reportDate: form.value.reportDate,
|
||||
originalText: originalText.value || recognizedText.value,
|
||||
aiFormattedContent: aiFormattedContent.value,
|
||||
userEditedContent: userEditedContent.value,
|
||||
inputType: inputType.value
|
||||
}
|
||||
});
|
||||
|
||||
ui.showToast({
|
||||
message: "日报提交成功",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
// 延迟跳转
|
||||
setTimeout(() => {
|
||||
router.back();
|
||||
}, 1000);
|
||||
} catch (error: any) {
|
||||
console.error("提交日报失败:", error);
|
||||
ui.showToast({
|
||||
message: "提交日报失败: " + (error.message || "未知错误"),
|
||||
type: "error"
|
||||
});
|
||||
} finally {
|
||||
isSubmitting.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.gap-2 {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
133
cool-unix/pages/demo/basic/button.uvue
Normal file
133
cool-unix/pages/demo/basic/button.uvue
Normal file
@@ -0,0 +1,133 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-button>{{ t("普通") }}</cl-button>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('不同类型')">
|
||||
<view class="flex flex-row flex-wrap mb-2 overflow-visible">
|
||||
<cl-button type="primary">{{ t("主要") }}</cl-button>
|
||||
<cl-button type="success">{{ t("成功") }}</cl-button>
|
||||
<cl-button type="warn">{{ t("警告") }}</cl-button>
|
||||
</view>
|
||||
|
||||
<view class="flex flex-row mb-2 overflow-visible">
|
||||
<cl-button type="error">{{ t("危险") }}</cl-button>
|
||||
<cl-button type="info">{{ t("信息") }}</cl-button>
|
||||
</view>
|
||||
|
||||
<view class="flex flex-row overflow-visible">
|
||||
<cl-button type="light">{{ t("浅色") }}</cl-button>
|
||||
<cl-button type="dark">{{ t("深色") }}</cl-button>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('只显示图标')">
|
||||
<view class="flex flex-row">
|
||||
<cl-button type="primary" icon="send-plane-fill"></cl-button>
|
||||
<cl-button type="error" icon="verified-badge-fill"></cl-button>
|
||||
<cl-button type="info" icon="edit-fill"></cl-button>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义')">
|
||||
<view class="flex flex-row justify-center mb-5 h-14 items-center">
|
||||
<cl-button
|
||||
:type="type"
|
||||
:size="size"
|
||||
:text="isText"
|
||||
:border="isBorder"
|
||||
:rounded="isRounded"
|
||||
:loading="isLoading"
|
||||
:disabled="isDisabled"
|
||||
:icon="isIcon ? 'send-plane-fill' : ''"
|
||||
:color="isColor ? '#4286e0' : ''"
|
||||
:pt="{
|
||||
className: parseClass([
|
||||
{
|
||||
'!bg-transparent': isColor
|
||||
}
|
||||
])
|
||||
}"
|
||||
>{{ t("自定义") }}</cl-button
|
||||
>
|
||||
</view>
|
||||
|
||||
<cl-list border>
|
||||
<view class="p-2">
|
||||
<cl-tabs
|
||||
v-model="size"
|
||||
:height="66"
|
||||
:list="sizeOptions"
|
||||
show-slider
|
||||
fill
|
||||
></cl-tabs>
|
||||
</view>
|
||||
|
||||
<cl-list-item :label="t('文本模式')">
|
||||
<cl-switch v-model="isText"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('带边框')">
|
||||
<cl-switch v-model="isBorder"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('圆角按钮')">
|
||||
<cl-switch v-model="isRounded"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('带左侧图标')">
|
||||
<cl-switch v-model="isIcon"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('加载中')">
|
||||
<cl-switch v-model="isLoading"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('禁用')">
|
||||
<cl-switch v-model="isDisabled"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('自定义颜色')">
|
||||
<cl-switch v-model="isColor"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import type { ClButtonType, ClTabsItem, Size } from "@/uni_modules/cool-ui";
|
||||
import { parseClass } from "@/cool";
|
||||
import { t } from "@/locale";
|
||||
|
||||
const type = ref<ClButtonType>("primary");
|
||||
|
||||
const isText = ref(false);
|
||||
const isBorder = ref(false);
|
||||
const isRounded = ref(false);
|
||||
const isLoading = ref(false);
|
||||
const isIcon = ref(false);
|
||||
const isDisabled = ref(false);
|
||||
const isColor = ref(false);
|
||||
|
||||
const size = ref<Size>("normal");
|
||||
const sizeOptions = ref<ClTabsItem[]>([
|
||||
{
|
||||
label: t("小"),
|
||||
value: "small"
|
||||
},
|
||||
{
|
||||
label: t("默认"),
|
||||
value: "normal"
|
||||
},
|
||||
{
|
||||
label: t("大"),
|
||||
value: "large"
|
||||
}
|
||||
]);
|
||||
</script>
|
||||
94
cool-unix/pages/demo/basic/icon.uvue
Normal file
94
cool-unix/pages/demo/basic/icon.uvue
Normal file
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('设置颜色')">
|
||||
<view class="flex flex-row">
|
||||
<cl-icon name="heart-fill" color="primary" class="mr-2"></cl-icon>
|
||||
<cl-icon name="heart-fill" color="success" class="mr-2"></cl-icon>
|
||||
<cl-icon name="heart-fill" color="error" class="mr-2"></cl-icon>
|
||||
<cl-icon name="heart-fill" color="warn" class="mr-2"></cl-icon>
|
||||
<cl-icon name="heart-fill" color="info" class="mr-2"></cl-icon>
|
||||
<cl-icon name="heart-fill" color="#428bca" class="mr-2"></cl-icon>
|
||||
<cl-icon name="heart-fill" color="purple"></cl-icon>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('设置大小')">
|
||||
<view class="flex flex-row">
|
||||
<cl-icon name="heart-fill" class="mr-2" :size="50"></cl-icon>
|
||||
<cl-icon name="heart-fill" class="mr-2" :size="40"></cl-icon>
|
||||
<cl-icon name="heart-fill" class="mr-2" :size="30"></cl-icon>
|
||||
<cl-icon name="heart-fill" class="mr-2" :size="20"></cl-icon>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item>
|
||||
<cl-text>{{ t("集成 iconfont 与 remixicon 图标库,展示部分示例") }}</cl-text>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('iconfont')">
|
||||
<cl-row :gutter="10">
|
||||
<cl-col :span="4" v-for="item in iconfont" :key="item">
|
||||
<view
|
||||
class="flex flex-col items-center justify-center h-[100rpx] rounded-lg"
|
||||
hover-class="opacity-60"
|
||||
:hover-stay-time="250"
|
||||
@tap="copy(item)"
|
||||
>
|
||||
<cl-icon :name="item"></cl-icon>
|
||||
</view>
|
||||
</cl-col>
|
||||
</cl-row>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('remixicon')">
|
||||
<cl-row :gutter="10">
|
||||
<cl-col :span="4" v-for="item in remixicon" :key="item">
|
||||
<view
|
||||
class="flex flex-col items-center justify-center h-[100rpx]"
|
||||
hover-class="opacity-60"
|
||||
:hover-stay-time="250"
|
||||
@tap="copy(item)"
|
||||
>
|
||||
<cl-icon :name="item"></cl-icon>
|
||||
</view>
|
||||
</cl-col>
|
||||
</cl-row>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { icons } from "@/icons";
|
||||
import { forInObject, keys } from "@/cool";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
import { t } from "@/locale";
|
||||
|
||||
const ui = useUi();
|
||||
|
||||
const remixicon = ref<string[]>([]);
|
||||
const iconfont = ref<string[]>([]);
|
||||
|
||||
forInObject(icons, (value, key) => {
|
||||
if (key == "iconfont") {
|
||||
iconfont.value = keys(value).slice(0, 100);
|
||||
} else {
|
||||
remixicon.value = keys(value).slice(0, 100);
|
||||
}
|
||||
});
|
||||
|
||||
function copy(data: string) {
|
||||
uni.setClipboardData({
|
||||
data,
|
||||
showToast: false,
|
||||
success() {
|
||||
ui.showToast({
|
||||
message: t("复制成功")
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
116
cool-unix/pages/demo/basic/image.uvue
Normal file
116
cool-unix/pages/demo/basic/image.uvue
Normal file
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-image :src="url"></cl-image>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('不同裁剪')">
|
||||
<view class="flex flex-row justify-between">
|
||||
<view class="flex flex-col items-center justify-center">
|
||||
<cl-image :src="url" mode="aspectFill"></cl-image>
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'text-sm mt-1'
|
||||
}"
|
||||
>aspectFill</cl-text
|
||||
>
|
||||
</view>
|
||||
|
||||
<view class="flex flex-col items-center justify-center">
|
||||
<cl-image :src="url" mode="aspectFit"></cl-image>
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'text-sm mt-1'
|
||||
}"
|
||||
>aspectFit</cl-text
|
||||
>
|
||||
</view>
|
||||
|
||||
<view class="flex flex-col items-center justify-center">
|
||||
<cl-image :src="url" mode="heightFix"></cl-image>
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'text-sm mt-1'
|
||||
}"
|
||||
>heightFix</cl-text
|
||||
>
|
||||
</view>
|
||||
|
||||
<view class="flex flex-col items-center justify-center">
|
||||
<cl-image :src="url" mode="scaleToFill"></cl-image>
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'text-sm mt-1'
|
||||
}"
|
||||
>scaleToFill</cl-text
|
||||
>
|
||||
</view>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('点击可预览')">
|
||||
<cl-image :src="url" preview></cl-image>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('失败时显示')">
|
||||
<cl-image src="url"></cl-image>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('加载中')">
|
||||
<cl-image src=""></cl-image>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义圆角')">
|
||||
<view class="flex flex-row">
|
||||
<cl-image
|
||||
:src="url"
|
||||
:pt="{
|
||||
inner: {
|
||||
className: '!rounded-none'
|
||||
}
|
||||
}"
|
||||
></cl-image>
|
||||
|
||||
<cl-image
|
||||
:src="url"
|
||||
:pt="{
|
||||
className: 'ml-3',
|
||||
inner: {
|
||||
className: '!rounded-2xl'
|
||||
}
|
||||
}"
|
||||
></cl-image>
|
||||
|
||||
<cl-image
|
||||
:src="url"
|
||||
:pt="{
|
||||
className: 'ml-3',
|
||||
inner: {
|
||||
className: '!rounded-3xl'
|
||||
}
|
||||
}"
|
||||
></cl-image>
|
||||
|
||||
<cl-image
|
||||
:src="url"
|
||||
:pt="{
|
||||
className: 'ml-3',
|
||||
inner: {
|
||||
className: '!rounded-full'
|
||||
}
|
||||
}"
|
||||
></cl-image>
|
||||
</view>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { t } from "@/locale";
|
||||
|
||||
const url = ref("https://unix.cool-js.com/images/demo/avatar.jpg");
|
||||
</script>
|
||||
72
cool-unix/pages/demo/basic/tag.uvue
Normal file
72
cool-unix/pages/demo/basic/tag.uvue
Normal file
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<view class="flex flex-row">
|
||||
<cl-tag>{{ t("标签") }}</cl-tag>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('不同类型')">
|
||||
<view class="flex flex-row flex-wrap">
|
||||
<cl-tag type="primary">{{ t("主要") }}</cl-tag>
|
||||
<cl-tag type="success">{{ t("成功") }}</cl-tag>
|
||||
<cl-tag type="warn">{{ t("警告") }}</cl-tag>
|
||||
<cl-tag type="error">{{ t("危险") }}</cl-tag>
|
||||
<cl-tag type="info">{{ t("信息") }}</cl-tag>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('带图标')">
|
||||
<view class="flex flex-row">
|
||||
<cl-tag icon="mail-line">{{ t("邮件") }}</cl-tag>
|
||||
<cl-tag icon="calendar-line">{{ t("日历") }}</cl-tag>
|
||||
<cl-tag icon="file-line">{{ t("文件") }}</cl-tag>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('圆角')">
|
||||
<view class="flex flex-row">
|
||||
<cl-tag rounded>{{ t("圆角") }}</cl-tag>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('可关闭')">
|
||||
<view class="flex flex-row">
|
||||
<cl-tag closable>{{ t("可关闭") }}</cl-tag>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('镂空')">
|
||||
<view class="flex flex-row flex-wrap">
|
||||
<cl-tag type="primary" plain>{{ t("主要") }}</cl-tag>
|
||||
<cl-tag type="success" plain>{{ t("成功") }}</cl-tag>
|
||||
<cl-tag type="warn" plain>{{ t("警告") }}</cl-tag>
|
||||
<cl-tag type="error" plain>{{ t("危险") }}</cl-tag>
|
||||
<cl-tag type="info" plain>{{ t("信息") }}</cl-tag>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义')">
|
||||
<view class="flex flex-row">
|
||||
<cl-tag
|
||||
:pt="{
|
||||
className: '!bg-sky-200',
|
||||
text: {
|
||||
className: '!text-sky-700'
|
||||
}
|
||||
}"
|
||||
>{{ t("自定义颜色") }}</cl-tag
|
||||
>
|
||||
|
||||
<cl-tag :pt="{ className: '!rounded-none' }">{{ t("自定义无圆角") }}</cl-tag>
|
||||
</view>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { t } from "@/locale";
|
||||
</script>
|
||||
103
cool-unix/pages/demo/basic/text.uvue
Normal file
103
cool-unix/pages/demo/basic/text.uvue
Normal file
@@ -0,0 +1,103 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-text>云想衣裳花想容,春风拂槛露华浓。</cl-text>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义颜色')">
|
||||
<cl-text color="primary">明月松间照,清泉石上流。</cl-text>
|
||||
<cl-text color="error">举头望明月,低头思故乡。</cl-text>
|
||||
<cl-text color="success">春眠不觉晓,处处闻啼鸟。</cl-text>
|
||||
<cl-text color="warn">劝君更尽一杯酒,西出阳关无故人。</cl-text>
|
||||
<cl-text color="info">孤帆远影碧空尽,唯见长江天际流。</cl-text>
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'text-sky-500'
|
||||
}"
|
||||
>大漠孤烟直,长河落日圆。</cl-text
|
||||
>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('省略号')">
|
||||
<cl-text ellipsis
|
||||
>云想衣裳花想容,春风拂槛露华浓。若非群玉山头见,会向瑶台月下逢。</cl-text
|
||||
>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('多行省略号')">
|
||||
<cl-text ellipsis :lines="2"
|
||||
>云想衣裳花想容,春风拂槛露华浓。若非群玉山头见,会向瑶台月下逢。天阶夜色凉如水,卧看牵牛织女星。人生若只如初见,何事秋风悲画扇。山有木兮木有枝,心悦君兮君不知。</cl-text
|
||||
>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('字体大小')">
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'text-xs'
|
||||
}"
|
||||
>text-xs</cl-text
|
||||
>
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'text-sm'
|
||||
}"
|
||||
>text-sm</cl-text
|
||||
>
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'text-md'
|
||||
}"
|
||||
>text-md</cl-text
|
||||
>
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'text-lg'
|
||||
}"
|
||||
>text-lg</cl-text
|
||||
>
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'text-xl'
|
||||
}"
|
||||
>text-xl</cl-text
|
||||
>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义尺寸')">
|
||||
<cl-text :size="20">20rpx</cl-text>
|
||||
<cl-text size="30rpx">30rpx</cl-text>
|
||||
<cl-text size="15px">15px</cl-text>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('金额')">
|
||||
<cl-text type="amount" :value="10000000.0"></cl-text>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('手机号脱敏')">
|
||||
<cl-text type="phone" mask value="13800138000"></cl-text>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('姓名脱敏')">
|
||||
<cl-text type="name" mask value="张三"></cl-text>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('邮箱脱敏')">
|
||||
<cl-text type="email" mask value="example@example.com"></cl-text>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('银行卡脱敏')">
|
||||
<cl-text type="card" mask value="1234 5678 9012 3456"></cl-text>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义脱敏字符')">
|
||||
<cl-text type="phone" value="13800138000" mask mask-char="~"></cl-text>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
</script>
|
||||
32
cool-unix/pages/demo/components/goods-item.uvue
Normal file
32
cool-unix/pages/demo/components/goods-item.uvue
Normal file
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<view class="p-3 pb-0">
|
||||
<view class="w-full p-3 bg-white rounded-xl dark:bg-surface-800">
|
||||
<cl-image :src="item?.image" mode="aspectFill" width="100%" height="280rpx"></cl-image>
|
||||
<cl-text :pt="{ className: 'mt-2' }">{{ item?.title }}</cl-text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
import { parse } from "@/cool";
|
||||
|
||||
defineOptions({
|
||||
name: "goods-item"
|
||||
});
|
||||
|
||||
type GoodsItem = {
|
||||
id: number;
|
||||
title: string;
|
||||
image: string;
|
||||
};
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
});
|
||||
|
||||
const item = computed(() => parse<GoodsItem>(props.value));
|
||||
</script>
|
||||
28
cool-unix/pages/demo/components/item.uvue
Normal file
28
cool-unix/pages/demo/components/item.uvue
Normal file
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<view class="demo-item dark:!bg-surface-800">
|
||||
<cl-text :pt="{ className: 'text-sm text-surface-400 mb-2' }" v-if="label != ''">{{
|
||||
label
|
||||
}}</cl-text>
|
||||
|
||||
<slot></slot>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
defineOptions({
|
||||
name: "demo-item"
|
||||
});
|
||||
|
||||
const props = defineProps({
|
||||
label: {
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.demo-item {
|
||||
@apply p-3 rounded-xl bg-white mb-3;
|
||||
}
|
||||
</style>
|
||||
17
cool-unix/pages/demo/components/tips.uvue
Normal file
17
cool-unix/pages/demo/components/tips.uvue
Normal file
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<view class="bg-surface-100 dark:!bg-surface-700 rounded-lg p-3 mb-3">
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'text-sm'
|
||||
}"
|
||||
>
|
||||
<slot></slot>
|
||||
</cl-text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineOptions({
|
||||
name: "demo-tips"
|
||||
});
|
||||
</script>
|
||||
32
cool-unix/pages/demo/data/avatar.uvue
Normal file
32
cool-unix/pages/demo/data/avatar.uvue
Normal file
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-avatar src="https://unix.cool-js.com/images/demo/avatar.jpg"></cl-avatar>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('无图片')">
|
||||
<cl-avatar></cl-avatar>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('圆角')">
|
||||
<cl-avatar
|
||||
rounded
|
||||
src="https://unix.cool-js.com/images/demo/avatar.jpg"
|
||||
></cl-avatar>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义大小')">
|
||||
<cl-avatar
|
||||
:size="120"
|
||||
src="https://unix.cool-js.com/images/demo/avatar.jpg"
|
||||
></cl-avatar>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
</script>
|
||||
59
cool-unix/pages/demo/data/banner.uvue
Normal file
59
cool-unix/pages/demo/data/banner.uvue
Normal file
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-banner :list="list"></cl-banner>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('禁用手势')">
|
||||
<cl-banner :list="list" :disable-touch="true"></cl-banner>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义样式')">
|
||||
<cl-banner
|
||||
:list="list"
|
||||
:pt="{
|
||||
className: 'mx-[-12rpx] !rounded-none',
|
||||
item: {
|
||||
className: parseClass(['scale-y-80 !px-[12rpx]'])
|
||||
},
|
||||
itemActive: {
|
||||
className: parseClass(['!scale-y-100'])
|
||||
},
|
||||
image: {
|
||||
className: '!rounded-none'
|
||||
}
|
||||
}"
|
||||
:previous-margin="40"
|
||||
:next-margin="40"
|
||||
></cl-banner>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义样式2')">
|
||||
<cl-banner
|
||||
:list="list"
|
||||
:pt="{
|
||||
className: 'mx-[-12rpx]',
|
||||
item: {
|
||||
className: parseClass(['px-[12rpx]'])
|
||||
}
|
||||
}"
|
||||
:next-margin="40"
|
||||
></cl-banner>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { ref } from "vue";
|
||||
import { parseClass } from "@/cool";
|
||||
|
||||
const list = ref<string[]>([
|
||||
"https://unix.cool-js.com/images/demo/bg1.png",
|
||||
"https://unix.cool-js.com/images/demo/bg2.png",
|
||||
"https://unix.cool-js.com/images/demo/bg3.png"
|
||||
]);
|
||||
</script>
|
||||
217
cool-unix/pages/demo/data/draggable.uvue
Normal file
217
cool-unix/pages/demo/data/draggable.uvue
Normal file
@@ -0,0 +1,217 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item>
|
||||
<cl-text color="info">
|
||||
{{ t("长按项即可拖动排序") }}
|
||||
</cl-text>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('单列排序')">
|
||||
<cl-draggable v-model="list">
|
||||
<template #item="{ item, index }">
|
||||
<view
|
||||
class="flex flex-row items-center p-3 bg-surface-100 rounded-lg mb-2 dark:!bg-surface-700"
|
||||
:class="{
|
||||
'opacity-50': item['disabled']
|
||||
}"
|
||||
>
|
||||
<cl-text>{{ (item as UTSJSONObject).label }}</cl-text>
|
||||
</view>
|
||||
</template>
|
||||
</cl-draggable>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('不需要长按')">
|
||||
<cl-draggable v-model="list5" :long-press="false">
|
||||
<template #item="{ item }">
|
||||
<view
|
||||
class="flex flex-row items-center p-3 bg-surface-100 rounded-lg mb-2 dark:!bg-surface-700"
|
||||
>
|
||||
<cl-text>{{ (item as UTSJSONObject).label }}</cl-text>
|
||||
</view>
|
||||
</template>
|
||||
</cl-draggable>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('结合列表使用')">
|
||||
<cl-list border>
|
||||
<cl-draggable v-model="list2">
|
||||
<template #item="{ item, index, dragging, dragIndex }">
|
||||
<cl-list-item
|
||||
icon="chat-thread-line"
|
||||
:label="(item as UTSJSONObject).label"
|
||||
arrow
|
||||
:pt="{
|
||||
inner: {
|
||||
className: parseClass([
|
||||
[
|
||||
dragging && dragIndex == index,
|
||||
isDark ? '!bg-surface-700' : '!bg-surface-100'
|
||||
]
|
||||
])
|
||||
}
|
||||
}"
|
||||
></cl-list-item>
|
||||
</template>
|
||||
</cl-draggable>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('多列排序')">
|
||||
<cl-draggable v-model="list3" :columns="4">
|
||||
<template #item="{ item, index }">
|
||||
<view
|
||||
class="flex flex-row items-center justify-center p-3 bg-surface-100 rounded-lg m-1 dark:!bg-surface-700"
|
||||
:class="{
|
||||
'opacity-50': item['disabled']
|
||||
}"
|
||||
>
|
||||
<cl-text>{{ (item as UTSJSONObject).label }}</cl-text>
|
||||
</view>
|
||||
</template>
|
||||
</cl-draggable>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('结合图片使用')">
|
||||
<cl-draggable v-model="list4" :columns="4">
|
||||
<template #item="{ item, index }">
|
||||
<view class="p-[2px]">
|
||||
<cl-image
|
||||
:src="(item as UTSJSONObject).url"
|
||||
mode="widthFix"
|
||||
:pt="{
|
||||
className: '!w-full'
|
||||
}"
|
||||
preview
|
||||
></cl-image>
|
||||
</view>
|
||||
</template>
|
||||
</cl-draggable>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { ref } from "vue";
|
||||
import { isDark, parseClass } from "@/cool";
|
||||
|
||||
// list:李白《将进酒》
|
||||
const list = ref<UTSJSONObject[]>([
|
||||
{
|
||||
label: "君不见黄河之水天上来"
|
||||
},
|
||||
{
|
||||
label: "奔流到海不复回",
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
label: "君不见高堂明镜悲白发"
|
||||
},
|
||||
{
|
||||
label: "朝如青丝暮成雪"
|
||||
},
|
||||
{
|
||||
label: "人生得意须尽欢"
|
||||
}
|
||||
]);
|
||||
|
||||
// list5:杜甫《春望》
|
||||
const list5 = ref<UTSJSONObject[]>([
|
||||
{
|
||||
label: "国破山河在"
|
||||
},
|
||||
{
|
||||
label: "城春草木深"
|
||||
},
|
||||
{
|
||||
label: "感时花溅泪"
|
||||
}
|
||||
]);
|
||||
|
||||
// list2:王之涣《登鹳雀楼》
|
||||
const list2 = ref<UTSJSONObject[]>([
|
||||
{
|
||||
label: "白日依山尽"
|
||||
},
|
||||
{
|
||||
label: "黄河入海流"
|
||||
},
|
||||
{
|
||||
label: "欲穷千里目"
|
||||
},
|
||||
{
|
||||
label: "更上一层楼"
|
||||
},
|
||||
{
|
||||
label: "一览众山小"
|
||||
}
|
||||
]);
|
||||
|
||||
const list3 = ref<UTSJSONObject[]>([
|
||||
{
|
||||
label: "项目1"
|
||||
},
|
||||
{
|
||||
label: "项目2"
|
||||
},
|
||||
{
|
||||
label: "项目3"
|
||||
},
|
||||
{
|
||||
label: "项目4"
|
||||
},
|
||||
{
|
||||
label: "项目5"
|
||||
},
|
||||
{
|
||||
label: "项目6"
|
||||
},
|
||||
{
|
||||
label: "项目7"
|
||||
},
|
||||
{
|
||||
label: "项目8",
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
label: "项目9"
|
||||
},
|
||||
{
|
||||
label: "项目10"
|
||||
},
|
||||
{
|
||||
label: "项目11"
|
||||
},
|
||||
{
|
||||
label: "项目12"
|
||||
}
|
||||
]);
|
||||
|
||||
const list4 = ref<UTSJSONObject[]>([
|
||||
{
|
||||
url: "https://unix.cool-js.com/images/demo/1.jpg"
|
||||
},
|
||||
{
|
||||
url: "https://unix.cool-js.com/images/demo/2.jpg"
|
||||
},
|
||||
{
|
||||
url: "https://unix.cool-js.com/images/demo/3.jpg"
|
||||
},
|
||||
{
|
||||
url: "https://unix.cool-js.com/images/demo/4.jpg"
|
||||
},
|
||||
{
|
||||
url: "https://unix.cool-js.com/images/demo/5.jpg"
|
||||
},
|
||||
{
|
||||
url: "https://unix.cool-js.com/images/demo/6.jpg"
|
||||
},
|
||||
{
|
||||
url: "https://unix.cool-js.com/images/demo/7.jpg"
|
||||
}
|
||||
]);
|
||||
</script>
|
||||
467
cool-unix/pages/demo/data/filter-bar.uvue
Normal file
467
cool-unix/pages/demo/data/filter-bar.uvue
Normal file
@@ -0,0 +1,467 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-filter-bar>
|
||||
<!-- 下拉框 -->
|
||||
<cl-filter-item
|
||||
label="综合排序"
|
||||
type="select"
|
||||
:value="1"
|
||||
:options="coreOptions"
|
||||
:pt="{
|
||||
className: 'w-[220rpx] !flex-none'
|
||||
}"
|
||||
@change="onOptionsChange"
|
||||
></cl-filter-item>
|
||||
|
||||
<!-- 排序 -->
|
||||
<cl-filter-item
|
||||
label="销量"
|
||||
type="sort"
|
||||
value="desc"
|
||||
@change="onSortChange"
|
||||
></cl-filter-item>
|
||||
|
||||
<!-- 开关 -->
|
||||
<cl-filter-item
|
||||
label="国补"
|
||||
type="switch"
|
||||
:value="false"
|
||||
@change="onSwitchChange"
|
||||
></cl-filter-item>
|
||||
|
||||
<!-- 自定义 -->
|
||||
<view
|
||||
class="flex flex-row items-center justify-center flex-1"
|
||||
@tap="openFilter"
|
||||
>
|
||||
<cl-text>筛选</cl-text>
|
||||
<cl-icon name="filter-line"></cl-icon>
|
||||
</view>
|
||||
</cl-filter-bar>
|
||||
</demo-item>
|
||||
|
||||
<demo-item>
|
||||
<cl-text pre-wrap :pt="{ className: 'text-sm p-2' }">{{
|
||||
JSON.stringify(filterForm, null, 4)
|
||||
}}</cl-text>
|
||||
</demo-item>
|
||||
|
||||
<demo-item>
|
||||
<cl-text pre-wrap :pt="{ className: 'text-sm p-2' }">{{
|
||||
JSON.stringify(searchForm, null, 4)
|
||||
}}</cl-text>
|
||||
</demo-item>
|
||||
</view>
|
||||
|
||||
<!-- 自定义筛选 -->
|
||||
<cl-popup
|
||||
v-model="filterVisible"
|
||||
:title="t('筛选')"
|
||||
direction="right"
|
||||
size="80%"
|
||||
:show-header="false"
|
||||
>
|
||||
<view class="flex flex-col h-full">
|
||||
<scroll-view class="flex-1">
|
||||
<cl-form :pt="{ className: 'p-3' }">
|
||||
<cl-form-item label="服务/折扣">
|
||||
<cl-row :gutter="20">
|
||||
<cl-col :span="8" v-for="(item, index) in disOptions" :key="index">
|
||||
<cl-checkbox
|
||||
v-model="searchForm.dis"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
:show-icon="false"
|
||||
:pt="{
|
||||
className: parseClass([
|
||||
'mb-3 p-2 rounded-lg justify-center border border-solid border-transparent',
|
||||
[isDark, 'bg-surface-800', 'bg-surface-100'],
|
||||
[
|
||||
searchForm.dis.includes(item.value),
|
||||
`${isDark ? '!bg-surface-700' : '!bg-white'} !border-primary-500`
|
||||
]
|
||||
]),
|
||||
label: {
|
||||
className: 'text-sm'
|
||||
}
|
||||
}"
|
||||
></cl-checkbox>
|
||||
</cl-col>
|
||||
</cl-row>
|
||||
</cl-form-item>
|
||||
|
||||
<cl-form-item label="价格区间">
|
||||
<view class="flex flex-row items-center">
|
||||
<cl-input
|
||||
v-model="searchForm.minPrice"
|
||||
type="digit"
|
||||
placeholder="最低价"
|
||||
:pt="{
|
||||
className: 'flex-1',
|
||||
inner: {
|
||||
className: 'text-center'
|
||||
}
|
||||
}"
|
||||
></cl-input>
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'px-2'
|
||||
}"
|
||||
>~</cl-text
|
||||
>
|
||||
<cl-input
|
||||
v-model="searchForm.maxPrice"
|
||||
type="digit"
|
||||
placeholder="最高价"
|
||||
:pt="{
|
||||
className: 'flex-1',
|
||||
inner: {
|
||||
className: 'text-center'
|
||||
}
|
||||
}"
|
||||
></cl-input>
|
||||
</view>
|
||||
</cl-form-item>
|
||||
|
||||
<cl-form-item label="品牌">
|
||||
<cl-row :gutter="20">
|
||||
<cl-col
|
||||
:span="8"
|
||||
v-for="(item, index) in brandOptions"
|
||||
:key="index"
|
||||
>
|
||||
<cl-checkbox
|
||||
v-model="searchForm.brand"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
:show-icon="false"
|
||||
:pt="{
|
||||
className: parseClass([
|
||||
'mb-3 p-2 rounded-lg justify-center border border-solid border-transparent',
|
||||
[isDark, 'bg-surface-800', 'bg-surface-100'],
|
||||
[
|
||||
searchForm.brand.includes(item.value),
|
||||
`${isDark ? '!bg-surface-700' : '!bg-white'} !border-primary-500`
|
||||
]
|
||||
]),
|
||||
label: {
|
||||
className: 'text-sm'
|
||||
}
|
||||
}"
|
||||
></cl-checkbox>
|
||||
</cl-col>
|
||||
</cl-row>
|
||||
</cl-form-item>
|
||||
|
||||
<cl-form-item label="内存">
|
||||
<cl-row :gutter="20">
|
||||
<cl-col
|
||||
:span="8"
|
||||
v-for="(item, index) in memoryOptions"
|
||||
:key="index"
|
||||
>
|
||||
<cl-radio
|
||||
v-model="searchForm.memory"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
:show-icon="false"
|
||||
:pt="{
|
||||
className: parseClass([
|
||||
'mb-3 p-2 rounded-lg justify-center border border-solid border-transparent',
|
||||
[isDark, 'bg-surface-800', 'bg-surface-100'],
|
||||
[
|
||||
searchForm.memory == item.value,
|
||||
`${isDark ? '!bg-surface-700' : '!bg-white'} !border-primary-500`
|
||||
]
|
||||
]),
|
||||
label: {
|
||||
className: 'text-sm'
|
||||
}
|
||||
}"
|
||||
></cl-radio>
|
||||
</cl-col>
|
||||
</cl-row>
|
||||
</cl-form-item>
|
||||
|
||||
<cl-form-item label="颜色">
|
||||
<cl-row :gutter="20">
|
||||
<cl-col
|
||||
:span="8"
|
||||
v-for="(item, index) in colorOptions"
|
||||
:key="index"
|
||||
>
|
||||
<cl-radio
|
||||
v-model="searchForm.color"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
:show-icon="false"
|
||||
:pt="{
|
||||
className: parseClass([
|
||||
'mb-3 p-2 rounded-lg justify-center border border-solid border-transparent',
|
||||
[isDark, 'bg-surface-800', 'bg-surface-100'],
|
||||
[
|
||||
searchForm.color == item.value,
|
||||
`${isDark ? '!bg-surface-700' : '!bg-white'} !border-primary-500`
|
||||
]
|
||||
]),
|
||||
label: {
|
||||
className: 'text-sm'
|
||||
}
|
||||
}"
|
||||
></cl-radio>
|
||||
</cl-col>
|
||||
</cl-row>
|
||||
</cl-form-item>
|
||||
</cl-form>
|
||||
</scroll-view>
|
||||
|
||||
<view class="flex flex-row p-3">
|
||||
<cl-button
|
||||
type="info"
|
||||
text
|
||||
border
|
||||
:pt="{
|
||||
className: 'flex-1'
|
||||
}"
|
||||
@tap="closeFilter"
|
||||
>{{ t("取消") }}</cl-button
|
||||
>
|
||||
<cl-button
|
||||
:pt="{
|
||||
className: 'flex-1'
|
||||
}"
|
||||
@tap="submit"
|
||||
>{{ t("确定") }}</cl-button
|
||||
>
|
||||
</view>
|
||||
|
||||
<cl-safe-area type="bottom"></cl-safe-area>
|
||||
</view>
|
||||
</cl-popup>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { reactive, ref } from "vue";
|
||||
import { useUi, type ClSelectOption } from "@/uni_modules/cool-ui";
|
||||
import { isDark, parseClass } from "@/cool";
|
||||
|
||||
const ui = useUi();
|
||||
|
||||
const filterVisible = ref(false);
|
||||
|
||||
function openFilter() {
|
||||
filterVisible.value = true;
|
||||
}
|
||||
|
||||
function closeFilter() {
|
||||
filterVisible.value = false;
|
||||
}
|
||||
|
||||
function submit() {
|
||||
closeFilter();
|
||||
|
||||
ui.showLoading();
|
||||
|
||||
setTimeout(() => {
|
||||
ui.hideLoading();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
const coreOptions = ref<ClSelectOption[]>([
|
||||
{
|
||||
label: "综合排序",
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: "价格从高到底",
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
label: "价格从低到高",
|
||||
value: 3
|
||||
}
|
||||
]);
|
||||
|
||||
type Option = {
|
||||
label: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
const disOptions = ref<Option[]>([
|
||||
{
|
||||
label: "百亿补贴",
|
||||
value: "billion_subsidy"
|
||||
},
|
||||
{
|
||||
label: "以旧换新",
|
||||
value: "trade_in"
|
||||
},
|
||||
{
|
||||
label: "分期免息",
|
||||
value: "installment"
|
||||
},
|
||||
{
|
||||
label: "包邮",
|
||||
value: "free_shipping"
|
||||
},
|
||||
{
|
||||
label: "促销",
|
||||
value: "promotion"
|
||||
},
|
||||
{
|
||||
label: "价保",
|
||||
value: "price_protection"
|
||||
},
|
||||
{
|
||||
label: "仅看有货",
|
||||
value: "in_stock"
|
||||
},
|
||||
{
|
||||
label: "货到付款",
|
||||
value: "cod"
|
||||
}
|
||||
]);
|
||||
|
||||
const brandOptions = ref<Option[]>([
|
||||
{
|
||||
label: "华为",
|
||||
value: "huawei"
|
||||
},
|
||||
{
|
||||
label: "苹果",
|
||||
value: "apple"
|
||||
},
|
||||
{
|
||||
label: "小米",
|
||||
value: "xiaomi"
|
||||
},
|
||||
{
|
||||
label: "三星",
|
||||
value: "samsung"
|
||||
},
|
||||
{
|
||||
label: "OPPO",
|
||||
value: "oppo"
|
||||
},
|
||||
{
|
||||
label: "vivo",
|
||||
value: "vivo"
|
||||
},
|
||||
{
|
||||
label: "荣耀",
|
||||
value: "honor"
|
||||
}
|
||||
]);
|
||||
|
||||
const colorOptions = ref<Option[]>([
|
||||
{
|
||||
label: "红色",
|
||||
value: "red"
|
||||
},
|
||||
{
|
||||
label: "蓝色",
|
||||
value: "blue"
|
||||
},
|
||||
{
|
||||
label: "黑色",
|
||||
value: "black"
|
||||
},
|
||||
{
|
||||
label: "白色",
|
||||
value: "white"
|
||||
},
|
||||
{
|
||||
label: "金色",
|
||||
value: "gold"
|
||||
},
|
||||
{
|
||||
label: "银色",
|
||||
value: "silver"
|
||||
},
|
||||
{
|
||||
label: "绿色",
|
||||
value: "green"
|
||||
},
|
||||
{
|
||||
label: "紫色",
|
||||
value: "purple"
|
||||
},
|
||||
{
|
||||
label: "灰色",
|
||||
value: "gray"
|
||||
},
|
||||
{
|
||||
label: "粉色",
|
||||
value: "pink"
|
||||
}
|
||||
]);
|
||||
|
||||
const memoryOptions = ref<Option[]>([
|
||||
{
|
||||
label: "128GB",
|
||||
value: "128"
|
||||
},
|
||||
{
|
||||
label: "256GB",
|
||||
value: "256"
|
||||
},
|
||||
{
|
||||
label: "512GB",
|
||||
value: "512"
|
||||
},
|
||||
{
|
||||
label: "1TB",
|
||||
value: "1024"
|
||||
}
|
||||
]);
|
||||
|
||||
type SearchForm = {
|
||||
dis: string[];
|
||||
minPrice: string;
|
||||
maxPrice: string;
|
||||
brand: string[];
|
||||
memory: string;
|
||||
color: string;
|
||||
};
|
||||
|
||||
const searchForm = ref<SearchForm>({
|
||||
dis: [],
|
||||
minPrice: "50",
|
||||
maxPrice: "300",
|
||||
brand: [],
|
||||
memory: "",
|
||||
color: ""
|
||||
});
|
||||
|
||||
type FilterForm = {
|
||||
core: number;
|
||||
sort: string;
|
||||
switch: boolean;
|
||||
};
|
||||
|
||||
const filterForm = reactive<FilterForm>({
|
||||
core: 0,
|
||||
sort: "none",
|
||||
switch: false
|
||||
});
|
||||
|
||||
function onOptionsChange(val: number) {
|
||||
console.log(val);
|
||||
filterForm.core = val;
|
||||
}
|
||||
|
||||
function onSortChange(val: string) {
|
||||
console.log(val);
|
||||
filterForm.sort = val;
|
||||
}
|
||||
|
||||
function onSwitchChange(val: boolean) {
|
||||
console.log(val);
|
||||
filterForm.switch = val;
|
||||
}
|
||||
</script>
|
||||
118
cool-unix/pages/demo/data/list-view-refresh.uvue
Normal file
118
cool-unix/pages/demo/data/list-view-refresh.uvue
Normal file
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<cl-list-view
|
||||
ref="listViewRef"
|
||||
:data="listView"
|
||||
:virtual="false"
|
||||
:pt="{
|
||||
refresher: {
|
||||
className: 'pt-3'
|
||||
}
|
||||
}"
|
||||
:refresher-enabled="true"
|
||||
@pull="onPull"
|
||||
@bottom="loadMore"
|
||||
>
|
||||
<template #item="{ value }">
|
||||
<goods-item :value="value"></goods-item>
|
||||
</template>
|
||||
|
||||
<template #bottom>
|
||||
<view class="py-3">
|
||||
<cl-loadmore
|
||||
v-if="list.length > 0"
|
||||
:loading="loading"
|
||||
safe-area-bottom
|
||||
></cl-loadmore>
|
||||
</view>
|
||||
</template>
|
||||
</cl-list-view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
import { ref } from "vue";
|
||||
import { usePager } from "@/cool";
|
||||
import GoodsItem from "../components/goods-item.uvue";
|
||||
import { t } from "@/locale";
|
||||
|
||||
const ui = useUi();
|
||||
|
||||
const listViewRef = ref<ClListViewComponentPublicInstance | null>(null);
|
||||
|
||||
let id = 0;
|
||||
|
||||
const { refresh, list, listView, loading, loadMore } = usePager((params, { render }) => {
|
||||
// 模拟请求
|
||||
setTimeout(() => {
|
||||
render({
|
||||
list: [
|
||||
{
|
||||
id: id++,
|
||||
title: "春日樱花盛开时节,粉色花瓣如诗如画般飘洒",
|
||||
image: "https://unix.cool-js.com/images/demo/1.jpg"
|
||||
},
|
||||
{
|
||||
id: id++,
|
||||
title: "夕阳西下的海滩边,金色阳光温柔地洒在波光粼粼的海面上,构成令人心旷神怡的日落美景",
|
||||
image: "https://unix.cool-js.com/images/demo/2.jpg"
|
||||
},
|
||||
{
|
||||
id: id++,
|
||||
title: "寒冬腊月时分,洁白雪花纷纷扬扬地覆盖着整个世界,感受冬日的宁静与美好",
|
||||
image: "https://unix.cool-js.com/images/demo/3.jpg"
|
||||
},
|
||||
{
|
||||
id: id++,
|
||||
title: "都市夜景霓虹闪烁,五彩斑斓光芒照亮城市营造梦幻般景象",
|
||||
image: "https://unix.cool-js.com/images/demo/5.jpg"
|
||||
},
|
||||
{
|
||||
id: id++,
|
||||
title: "云雾缭绕的山间风光如诗如画让人心旷神怡,微风轻抚树梢带来阵阵清香,鸟儿在林间自由歌唱",
|
||||
image: "https://unix.cool-js.com/images/demo/6.jpg"
|
||||
},
|
||||
{
|
||||
id: id++,
|
||||
title: "古老建筑与现代摩天大楼交相辉映,传统与现代完美融合创造独特城市景观",
|
||||
image: "https://unix.cool-js.com/images/demo/7.jpg"
|
||||
},
|
||||
{
|
||||
id: id++,
|
||||
title: "广袤田野绿意盎然风光无限,金黄麦浪在微风中轻柔摇曳,农家炊烟袅袅升起",
|
||||
image: "https://unix.cool-js.com/images/demo/8.jpg"
|
||||
},
|
||||
{
|
||||
id: id++,
|
||||
title: "璀璨星空下银河横跨天际,繁星闪烁神秘光芒营造浪漫夜空美景",
|
||||
image: "https://unix.cool-js.com/images/demo/9.jpg"
|
||||
},
|
||||
{
|
||||
id: id++,
|
||||
title: "雄伟瀑布从高耸悬崖飞流直下激起千层浪花,彩虹在水雾中若隐若现如梦如幻",
|
||||
image: "https://unix.cool-js.com/images/demo/10.jpg"
|
||||
}
|
||||
],
|
||||
pagination: {
|
||||
page: params["page"],
|
||||
size: params["size"],
|
||||
total: 100
|
||||
}
|
||||
});
|
||||
|
||||
ui.hideLoading();
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
async function onPull() {
|
||||
await refresh({ page: 1 });
|
||||
listViewRef.value!.stopRefresh();
|
||||
}
|
||||
|
||||
onReady(() => {
|
||||
ui.showLoading(t("加载中"));
|
||||
// 默认请求
|
||||
refresh({});
|
||||
});
|
||||
</script>
|
||||
68
cool-unix/pages/demo/data/list-view.uvue
Normal file
68
cool-unix/pages/demo/data/list-view.uvue
Normal file
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="page">
|
||||
<view class="p-3 pb-0">
|
||||
<demo-item>
|
||||
<cl-text
|
||||
>采用虚拟列表技术实现高性能渲染,支持海量数据无限滚动,当前演示数据规模:{{
|
||||
data.length
|
||||
}}条</cl-text
|
||||
>
|
||||
</demo-item>
|
||||
</view>
|
||||
|
||||
<view class="list">
|
||||
<cl-list-view
|
||||
:data="data"
|
||||
:pt="{
|
||||
indexBar: {
|
||||
className: '!fixed'
|
||||
},
|
||||
itemHover: {
|
||||
className: 'bg-gray-200'
|
||||
}
|
||||
}"
|
||||
>
|
||||
</cl-list-view>
|
||||
</view>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { request } from "@/cool";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { useListView, useUi, type ClListViewItem } from "@/uni_modules/cool-ui";
|
||||
import { ref } from "vue";
|
||||
|
||||
const ui = useUi();
|
||||
|
||||
const data = ref<ClListViewItem[]>([]);
|
||||
|
||||
onReady(() => {
|
||||
ui.showLoading();
|
||||
|
||||
request({
|
||||
url: "https://unix.cool-js.com/data/pca_flat.json"
|
||||
})
|
||||
.then((res) => {
|
||||
data.value = useListView(res as UTSJSONObject[]);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
})
|
||||
.finally(() => {
|
||||
ui.hideLoading();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page {
|
||||
height: 100%;
|
||||
|
||||
.list {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
120
cool-unix/pages/demo/data/list.uvue
Normal file
120
cool-unix/pages/demo/data/list.uvue
Normal file
@@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-list-item :label="t('用户名')">
|
||||
<cl-text>神仙都没用</cl-text>
|
||||
</cl-list-item>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('内容靠左')">
|
||||
<cl-list-item
|
||||
:label="t('QQ')"
|
||||
justify="start"
|
||||
:pt="{
|
||||
label: {
|
||||
className: '!w-10'
|
||||
}
|
||||
}"
|
||||
>
|
||||
<cl-text>615206459</cl-text>
|
||||
</cl-list-item>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('带箭头')">
|
||||
<cl-list-item label="年龄" arrow>
|
||||
<cl-text>18</cl-text>
|
||||
</cl-list-item>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('带图标')">
|
||||
<cl-list-item :label="t('余额')" icon="wallet-line">
|
||||
<cl-text>10,9000</cl-text>
|
||||
</cl-list-item>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('带图片')">
|
||||
<cl-list-item
|
||||
arrow
|
||||
:pt="{
|
||||
image: {
|
||||
width: 48,
|
||||
height: 48
|
||||
}
|
||||
}"
|
||||
:label="t('神仙都没用')"
|
||||
image="https://unix.cool-js.com/images/demo/avatar.jpg"
|
||||
>
|
||||
</cl-list-item>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('折叠')">
|
||||
<cl-list-item :label="t('点击展开')" collapse arrow>
|
||||
<template #collapse>
|
||||
<view class="bg-surface-100 dark:bg-surface-700 p-3 rounded-xl">
|
||||
<cl-text
|
||||
>云想衣裳花想容,春风拂槛露华浓。若非群玉山头见,会向瑶台月下逢。</cl-text
|
||||
>
|
||||
</view>
|
||||
</template>
|
||||
</cl-list-item>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('可滑动')">
|
||||
<cl-list-item :label="t('左滑编辑')" swipeable>
|
||||
<template #swipe-right>
|
||||
<view
|
||||
class="bg-green-500 w-20 h-full flex flex-row items-center justify-center"
|
||||
>
|
||||
<text class="text-white text-md">{{ t("编辑") }}</text>
|
||||
</view>
|
||||
</template>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item ref="listItemRef" :label="t('右滑删除')" swipeable>
|
||||
<template #swipe-left>
|
||||
<view
|
||||
class="bg-red-500 w-20 h-full flex flex-row items-center justify-center"
|
||||
@tap="onDelete"
|
||||
>
|
||||
<text class="text-white text-md">{{ t("删除") }}</text>
|
||||
</view>
|
||||
</template>
|
||||
</cl-list-item>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('禁用')">
|
||||
<cl-list-item :label="t('账号')" disabled>
|
||||
<cl-text>1234567890</cl-text>
|
||||
</cl-list-item>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('列表')">
|
||||
<cl-list border>
|
||||
<cl-list-item :label="t('我的订单')" hoverable> </cl-list-item>
|
||||
<cl-list-item :label="t('我的收藏')" hoverable> </cl-list-item>
|
||||
<cl-list-item :label="t('我的钱包')" hoverable> </cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
import { ref } from "vue";
|
||||
|
||||
const ui = useUi();
|
||||
|
||||
const listItemRef = ref<ClListItemComponentPublicInstance | null>(null);
|
||||
|
||||
function onDelete() {
|
||||
ui.showToast({
|
||||
message: "删除成功"
|
||||
});
|
||||
|
||||
listItemRef.value!.resetSwipe();
|
||||
}
|
||||
</script>
|
||||
70
cool-unix/pages/demo/data/marquee.uvue
Normal file
70
cool-unix/pages/demo/data/marquee.uvue
Normal file
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('横向滚动')">
|
||||
<cl-marquee
|
||||
:list="list"
|
||||
direction="horizontal"
|
||||
:item-height="200"
|
||||
:item-width="360"
|
||||
:pt="{
|
||||
className: 'h-[200rpx] rounded-xl'
|
||||
}"
|
||||
></cl-marquee>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('纵向滚动')">
|
||||
<cl-marquee
|
||||
ref="marqueeRef"
|
||||
:list="list"
|
||||
direction="vertical"
|
||||
:item-height="260"
|
||||
:duration="isSpeed ? 2000 : 5000"
|
||||
:pt="{
|
||||
className: 'h-[500rpx] rounded-xl'
|
||||
}"
|
||||
></cl-marquee>
|
||||
|
||||
<cl-list
|
||||
border
|
||||
:pt="{
|
||||
className: 'mt-5'
|
||||
}"
|
||||
>
|
||||
<cl-list-item :label="t('快一点')">
|
||||
<cl-switch v-model="isSpeed"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('暂停')">
|
||||
<cl-switch v-model="isPause" @change="onPauseChange"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { t } from "@/locale";
|
||||
import { ref } from "vue";
|
||||
|
||||
const marqueeRef = ref<ClMarqueeComponentPublicInstance | null>(null);
|
||||
|
||||
const list = ref<string[]>([
|
||||
"https://unix.cool-js.com/images/demo/bg1.png",
|
||||
"https://unix.cool-js.com/images/demo/bg2.png",
|
||||
"https://unix.cool-js.com/images/demo/bg3.png"
|
||||
]);
|
||||
|
||||
const isSpeed = ref(false);
|
||||
const isPause = ref(false);
|
||||
|
||||
function onPauseChange(value: boolean) {
|
||||
if (value) {
|
||||
marqueeRef.value!.pause();
|
||||
} else {
|
||||
marqueeRef.value!.play();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
70
cool-unix/pages/demo/data/pagination.uvue
Normal file
70
cool-unix/pages/demo/data/pagination.uvue
Normal file
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-pagination v-model="page1" :total="24"> </cl-pagination>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('多页数')">
|
||||
<cl-pagination v-model="page2" :total="500"> </cl-pagination>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义样式')">
|
||||
<cl-pagination
|
||||
v-model="page3"
|
||||
:total="100"
|
||||
:pt="{
|
||||
item: {
|
||||
className: '!rounded-none !mx-[2rpx]'
|
||||
}
|
||||
}"
|
||||
>
|
||||
</cl-pagination>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义文本')">
|
||||
<cl-pagination
|
||||
v-model="page4"
|
||||
:total="24"
|
||||
:pt="{
|
||||
prev: {
|
||||
className: '!w-auto px-3'
|
||||
},
|
||||
next: {
|
||||
className: '!w-auto px-3'
|
||||
}
|
||||
}"
|
||||
>
|
||||
<template #prev>
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'text-sm'
|
||||
}"
|
||||
>{{ t("上一页") }}</cl-text
|
||||
>
|
||||
</template>
|
||||
|
||||
<template #next>
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'text-sm'
|
||||
}"
|
||||
>{{ t("下一页") }}</cl-text
|
||||
>
|
||||
</template>
|
||||
</cl-pagination>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
|
||||
const page1 = ref(1);
|
||||
const page2 = ref(13);
|
||||
const page3 = ref(1);
|
||||
const page4 = ref(1);
|
||||
</script>
|
||||
110
cool-unix/pages/demo/data/read-more.uvue
Normal file
110
cool-unix/pages/demo/data/read-more.uvue
Normal file
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-read-more>
|
||||
<cl-text>
|
||||
云想衣裳花想容,春风拂槛露华浓。若非群玉山头见,会向瑶台月下逢。
|
||||
一枝红艳露凝香,云雨巫山枉断肠。借问汉宫谁得似?可怜飞燕倚新妆。
|
||||
名花倾国两相欢,常得君王带笑看。解释春风无限恨,沉香亭北倚阑干。
|
||||
</cl-text>
|
||||
</cl-read-more>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('禁用切换按钮')">
|
||||
<cl-read-more
|
||||
v-model="visible"
|
||||
:disabled="disabled"
|
||||
:expand-text="disabled ? '付费解锁' : '展开'"
|
||||
:expand-icon="disabled ? 'lock-line' : 'arrow-down-s-line'"
|
||||
@toggle="toggle"
|
||||
>
|
||||
<cl-text>
|
||||
云想衣裳花想容,春风拂槛露华浓。若非群玉山头见,会向瑶台月下逢。
|
||||
一枝红艳露凝香,云雨巫山枉断肠。借问汉宫谁得似?可怜飞燕倚新妆。
|
||||
名花倾国两相欢,常得君王带笑看。解释春风无限恨,沉香亭北倚阑干。
|
||||
</cl-text>
|
||||
</cl-read-more>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('动态内容')">
|
||||
<cl-read-more :content="content" :show-toggle="content != ''" ref="readMoreRef">
|
||||
<view
|
||||
class="flex flex-row items-center justify-center h-14"
|
||||
v-if="content == ''"
|
||||
>
|
||||
<cl-loading :size="32"></cl-loading>
|
||||
</view>
|
||||
</cl-read-more>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义高度')">
|
||||
<cl-read-more :height="300">
|
||||
<cl-text>
|
||||
云想衣裳花想容,春风拂槛露华浓。若非群玉山头见,会向瑶台月下逢。
|
||||
一枝红艳露凝香,云雨巫山枉断肠。借问汉宫谁得似?可怜飞燕倚新妆。
|
||||
</cl-text>
|
||||
|
||||
<cl-image
|
||||
:height="300"
|
||||
width="100%"
|
||||
:pt="{
|
||||
className: 'my-3'
|
||||
}"
|
||||
src="https://unix.cool-js.com/images/demo/bg1.png"
|
||||
></cl-image>
|
||||
|
||||
<cl-text>
|
||||
名花倾国两相欢,常得君王带笑看。解释春风无限恨,沉香亭北倚阑干。
|
||||
</cl-text>
|
||||
</cl-read-more>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { ref } from "vue";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
|
||||
const ui = useUi();
|
||||
|
||||
const visible = ref(false);
|
||||
const disabled = ref(true);
|
||||
|
||||
const readMoreRef = ref<ClReadMoreComponentPublicInstance | null>(null);
|
||||
const content = ref("");
|
||||
|
||||
function getContent() {
|
||||
setTimeout(() => {
|
||||
content.value =
|
||||
"云想衣裳花想容,春风拂槛露华浓。若非群玉山头见,会向瑶台月下逢。一枝红艳露凝香,云雨巫山枉断肠。借问汉宫谁得似?可怜飞燕倚新妆。名花倾国两相欢,常得君王带笑看。解释春风无限恨,沉香亭北倚阑干。";
|
||||
|
||||
// 使用 slot 插入内容时,如果内容发生变化,需要重新获取高度
|
||||
// readMoreRef.value!.getContentHeight();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function toggle(isExpanded: boolean) {
|
||||
ui.showConfirm({
|
||||
title: "提示",
|
||||
message: "需支付100元才能解锁全部内容,是否继续?",
|
||||
callback(action) {
|
||||
if (action == "confirm") {
|
||||
ui.showToast({
|
||||
message: "支付成功"
|
||||
});
|
||||
|
||||
disabled.value = false;
|
||||
visible.value = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onReady(() => {
|
||||
getContent();
|
||||
});
|
||||
</script>
|
||||
71
cool-unix/pages/demo/data/timeline.uvue
Normal file
71
cool-unix/pages/demo/data/timeline.uvue
Normal file
@@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-timeline>
|
||||
<cl-timeline-item
|
||||
icon="account-box-line"
|
||||
:title="t('开通账号')"
|
||||
date="2025-01-01"
|
||||
:content="t('赠送500元')"
|
||||
>
|
||||
</cl-timeline-item>
|
||||
|
||||
<cl-timeline-item
|
||||
icon="id-card-line"
|
||||
:title="t('完成实名认证')"
|
||||
date="2025-01-02"
|
||||
:content="t('通过身份证认证')"
|
||||
>
|
||||
</cl-timeline-item>
|
||||
|
||||
<cl-timeline-item
|
||||
icon="bank-card-line"
|
||||
:title="t('绑定银行卡')"
|
||||
date="2025-01-03"
|
||||
:content="t('绑定招商银行储蓄卡')"
|
||||
>
|
||||
</cl-timeline-item>
|
||||
|
||||
<cl-timeline-item
|
||||
icon="money-cny-box-line"
|
||||
:title="t('首次充值')"
|
||||
date="2025-01-04"
|
||||
:content="t('充值1000元')"
|
||||
>
|
||||
</cl-timeline-item>
|
||||
|
||||
<cl-timeline-item
|
||||
icon="checkbox-line"
|
||||
:title="t('完成首笔交易')"
|
||||
date="2025-01-05"
|
||||
:hide-line="true"
|
||||
>
|
||||
<view class="flex flex-row mb-3 mt-1">
|
||||
<cl-image
|
||||
src="https://unix.cool-js.com/images/demo/bg1.png"
|
||||
></cl-image>
|
||||
|
||||
<view class="flex-1 px-3">
|
||||
<cl-text>{{ t("优选灵活配置混合A") }}</cl-text>
|
||||
|
||||
<cl-text class="mr-5 mt-1 text-sm">{{ t("1000元起") }}</cl-text>
|
||||
|
||||
<view class="flex flex-row mt-2 items-center">
|
||||
<cl-button size="small" type="light">{{
|
||||
t("立即购买")
|
||||
}}</cl-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</cl-timeline-item>
|
||||
</cl-timeline>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
</script>
|
||||
391
cool-unix/pages/demo/data/tree.uvue
Normal file
391
cool-unix/pages/demo/data/tree.uvue
Normal file
@@ -0,0 +1,391 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('树形结构')">
|
||||
<cl-tree
|
||||
v-model="checkedKeys"
|
||||
ref="treeRef"
|
||||
:list="list"
|
||||
:icon="isCustomIcon ? 'add-circle-line' : 'arrow-right-s-fill'"
|
||||
:expand-icon="isCustomIcon ? 'indeterminate-circle-line' : 'arrow-down-s-fill'"
|
||||
:checkable="true"
|
||||
:multiple="true"
|
||||
:check-strictly="checkStrictly"
|
||||
></cl-tree>
|
||||
|
||||
<cl-list border :pt="{ className: 'mt-5' }">
|
||||
<cl-list-item :label="t('父子关联')">
|
||||
<cl-switch v-model="checkStrictly"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('换个图标')">
|
||||
<cl-switch v-model="isCustomIcon"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('选中值')">
|
||||
<cl-text>{{ checkedKeys.join("、") }}</cl-text>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('选中操作')">
|
||||
<view class="mb-2">
|
||||
<cl-button @tap="setChecked">{{ t("选中部分节点") }}</cl-button>
|
||||
</view>
|
||||
|
||||
<view class="mb-2">
|
||||
<cl-button @tap="getChecked">{{ t("获取选中节点") }}</cl-button>
|
||||
</view>
|
||||
|
||||
<view class="mb-2">
|
||||
<cl-button @tap="getHalfChecked">{{ t("获取半选节点") }}</cl-button>
|
||||
|
||||
<cl-text
|
||||
v-if="halfCheckedKeys.length > 0"
|
||||
:pt="{
|
||||
className: 'text-sm p-2'
|
||||
}"
|
||||
>{{ halfCheckedKeys.join("、") }}</cl-text
|
||||
>
|
||||
</view>
|
||||
|
||||
<view class="mb-2">
|
||||
<cl-button @tap="clearChecked">{{ t("清空选中") }}</cl-button>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('展开操作')">
|
||||
<view class="mb-2">
|
||||
<cl-button @tap="expand">{{ t("展开部分节点") }}</cl-button>
|
||||
</view>
|
||||
|
||||
<view class="mb-2">
|
||||
<cl-button @tap="getExpanded">{{ t("获取展开节点") }}</cl-button>
|
||||
|
||||
<cl-text
|
||||
v-if="expandedKeys.length > 0"
|
||||
:pt="{
|
||||
className: 'text-sm p-2'
|
||||
}"
|
||||
>{{ expandedKeys.join("、") }}</cl-text
|
||||
>
|
||||
</view>
|
||||
|
||||
<view class="mb-2">
|
||||
<cl-button @tap="expandAll">{{ t("展开所有") }}</cl-button>
|
||||
</view>
|
||||
|
||||
<view class="mb-2">
|
||||
<cl-button @tap="collapseAll">{{ t("收起所有") }}</cl-button>
|
||||
</view>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { t } from "@/locale";
|
||||
import { ref } from "vue";
|
||||
import { useTree, useUi, type ClTreeItem } from "@/uni_modules/cool-ui";
|
||||
|
||||
const ui = useUi();
|
||||
|
||||
const list = ref<ClTreeItem[]>([]);
|
||||
|
||||
function refresh() {
|
||||
ui.showLoading();
|
||||
|
||||
setTimeout(() => {
|
||||
list.value = useTree([
|
||||
{
|
||||
id: "1",
|
||||
label: "华为",
|
||||
children: [
|
||||
{
|
||||
id: "1-1",
|
||||
label: "手机",
|
||||
children: [
|
||||
{
|
||||
id: "1-1-1",
|
||||
label: "Mate系列",
|
||||
children: [
|
||||
{
|
||||
id: "1-1-1-1",
|
||||
label: "Mate 50"
|
||||
},
|
||||
{
|
||||
id: "1-1-1-2",
|
||||
disabled: true,
|
||||
label: "Mate 40"
|
||||
},
|
||||
{
|
||||
id: "1-1-1-3",
|
||||
label: "Mate 30"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "1-1-2",
|
||||
label: "P系列",
|
||||
children: [
|
||||
{
|
||||
id: "1-1-2-1",
|
||||
disabled: true,
|
||||
label: "P60"
|
||||
},
|
||||
{
|
||||
id: "1-1-2-2",
|
||||
label: "P50"
|
||||
},
|
||||
{
|
||||
id: "1-1-2-3",
|
||||
label: "P40"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "1-2",
|
||||
label: "笔记本",
|
||||
children: [
|
||||
{
|
||||
id: "1-2-1",
|
||||
label: "MateBook X",
|
||||
children: [
|
||||
{
|
||||
id: "1-2-1-1",
|
||||
label: "MateBook X Pro"
|
||||
},
|
||||
{
|
||||
id: "1-2-1-2",
|
||||
label: "MateBook X 2022"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "1-2-2",
|
||||
label: "MateBook D",
|
||||
children: [
|
||||
{
|
||||
id: "1-2-2-1",
|
||||
label: "MateBook D 14"
|
||||
},
|
||||
{
|
||||
id: "1-2-2-2",
|
||||
label: "MateBook D 15"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "1-2-3",
|
||||
label: "MateBook 13"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
label: "小米",
|
||||
isExpand: true,
|
||||
children: [
|
||||
{
|
||||
id: "2-1",
|
||||
label: "手机",
|
||||
children: [
|
||||
{
|
||||
id: "2-1-1",
|
||||
label: "小米数字系列"
|
||||
},
|
||||
{
|
||||
id: "2-1-2",
|
||||
label: "Redmi系列"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "2-2",
|
||||
label: "家电",
|
||||
children: [
|
||||
{
|
||||
id: "2-2-1",
|
||||
label: "电视"
|
||||
},
|
||||
{
|
||||
id: "2-2-2",
|
||||
label: "空调"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
label: "苹果",
|
||||
children: [
|
||||
{
|
||||
id: "3-1",
|
||||
label: "手机",
|
||||
children: [
|
||||
{
|
||||
id: "3-1-1",
|
||||
label: "iPhone 14"
|
||||
},
|
||||
{
|
||||
id: "3-1-2",
|
||||
label: "iPhone 13"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "3-2",
|
||||
label: "平板",
|
||||
children: [
|
||||
{
|
||||
id: "3-2-1",
|
||||
label: "iPad Pro"
|
||||
},
|
||||
{
|
||||
id: "3-2-2",
|
||||
label: "iPad Air"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
label: "OPPO",
|
||||
children: [
|
||||
{
|
||||
id: "4-1",
|
||||
label: "手机",
|
||||
children: [
|
||||
{
|
||||
id: "4-1-1",
|
||||
label: "Find系列"
|
||||
},
|
||||
{
|
||||
id: "4-1-2",
|
||||
label: "Reno系列"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "4-2",
|
||||
label: "配件",
|
||||
children: [
|
||||
{
|
||||
id: "4-2-1",
|
||||
label: "耳机"
|
||||
},
|
||||
{
|
||||
id: "4-2-2",
|
||||
label: "手环"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "5",
|
||||
label: "vivo",
|
||||
children: [
|
||||
{
|
||||
id: "5-1",
|
||||
label: "手机",
|
||||
children: [
|
||||
{
|
||||
id: "5-1-1",
|
||||
label: "X系列"
|
||||
},
|
||||
{
|
||||
id: "5-1-2",
|
||||
label: "S系列"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "5-2",
|
||||
label: "智能设备",
|
||||
children: [
|
||||
{
|
||||
id: "5-2-1",
|
||||
label: "手表"
|
||||
},
|
||||
{
|
||||
id: "5-2-2",
|
||||
label: "耳机"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
ui.hideLoading();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
// 是否严格的遵循父子不互相关联
|
||||
const checkStrictly = ref(false);
|
||||
|
||||
// 是否自定义图标
|
||||
const isCustomIcon = ref(false);
|
||||
|
||||
// 树形组件引用
|
||||
const treeRef = ref<ClTreeComponentPublicInstance | null>(null);
|
||||
|
||||
// 选中节点的keys
|
||||
const checkedKeys = ref<(string | number)[]>(["1-1-1-1", "2-1-1", "2-1-2"]);
|
||||
const checkedKeys2 = ref<string | null>("1-1-1");
|
||||
|
||||
// 半选节点的keys
|
||||
const halfCheckedKeys = ref<(string | number)[]>([]);
|
||||
|
||||
// 展开节点的keys
|
||||
const expandedKeys = ref<(string | number)[]>([]);
|
||||
|
||||
// 演示方法
|
||||
function setChecked() {
|
||||
treeRef.value!.setCheckedKeys(["1-1", "2"]);
|
||||
}
|
||||
|
||||
function getChecked() {
|
||||
checkedKeys.value = treeRef.value!.getCheckedKeys();
|
||||
}
|
||||
|
||||
function clearChecked() {
|
||||
treeRef.value!.clearChecked();
|
||||
checkedKeys.value = [];
|
||||
halfCheckedKeys.value = [];
|
||||
}
|
||||
|
||||
function getHalfChecked() {
|
||||
halfCheckedKeys.value = treeRef.value!.getHalfCheckedKeys();
|
||||
}
|
||||
|
||||
function expand() {
|
||||
treeRef.value!.setExpandedKeys(["4", "5"]);
|
||||
}
|
||||
|
||||
function getExpanded() {
|
||||
expandedKeys.value = treeRef.value!.getExpandedKeys();
|
||||
}
|
||||
|
||||
function expandAll() {
|
||||
treeRef.value!.expandAll();
|
||||
expandedKeys.value = treeRef.value!.getExpandedKeys();
|
||||
}
|
||||
|
||||
function collapseAll() {
|
||||
treeRef.value!.collapseAll();
|
||||
expandedKeys.value = [];
|
||||
}
|
||||
|
||||
onReady(() => {
|
||||
refresh();
|
||||
});
|
||||
</script>
|
||||
138
cool-unix/pages/demo/data/waterfall.uvue
Normal file
138
cool-unix/pages/demo/data/waterfall.uvue
Normal file
@@ -0,0 +1,138 @@
|
||||
<template>
|
||||
<cl-page back-top>
|
||||
<view class="py-2">
|
||||
<cl-waterfall ref="waterfallRef" :column="2" :gutter="16">
|
||||
<template #item="{ item, index }">
|
||||
<view class="bg-white mb-3 rounded-xl dark:!bg-gray-800 relative">
|
||||
<image
|
||||
:src="item['image']"
|
||||
mode="widthFix"
|
||||
class="w-full rounded-xl"
|
||||
></image>
|
||||
|
||||
<template v-if="item['isAd']">
|
||||
<cl-tag :pt="{ className: 'absolute left-1 top-1 scale-75' }"
|
||||
>广告</cl-tag
|
||||
>
|
||||
<cl-icon
|
||||
color="white"
|
||||
name="close-line"
|
||||
:pt="{ className: 'absolute right-2 top-2' }"
|
||||
@tap="del(item['id'] as number)"
|
||||
></cl-icon>
|
||||
</template>
|
||||
|
||||
<view class="p-3" v-else>
|
||||
<cl-text>{{ item["title"] }}</cl-text>
|
||||
|
||||
<cl-row class="mt-2" :pt="{ className: 'justify-end items-center' }">
|
||||
<cl-icon name="heart-line"></cl-icon>
|
||||
<cl-text :pt="{ className: 'text-sm ml-1' }">{{
|
||||
item["likeCount"]
|
||||
}}</cl-text>
|
||||
</cl-row>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</cl-waterfall>
|
||||
|
||||
<cl-loadmore :loading="true" safe-area-bottom></cl-loadmore>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { random } from "@/cool";
|
||||
import { onMounted, ref } from "vue";
|
||||
|
||||
const waterfallRef = ref<ClWaterfallComponentPublicInstance | null>(null);
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
let id = 0;
|
||||
|
||||
function refresh() {
|
||||
const items = [
|
||||
{
|
||||
id: id++,
|
||||
likeCount: random(100, 1000),
|
||||
title: "春日樱花盛开时节,粉色花瓣如诗如画般飘洒",
|
||||
image: "https://unix.cool-js.com/images/demo/1.jpg"
|
||||
},
|
||||
{
|
||||
id: id++,
|
||||
likeCount: random(100, 1000),
|
||||
title: "夕阳西下的海滩边,金色阳光温柔地洒在波光粼粼的海面上,构成令人心旷神怡的日落美景",
|
||||
image: "https://unix.cool-js.com/images/demo/2.jpg"
|
||||
},
|
||||
{
|
||||
id: id++,
|
||||
likeCount: random(100, 1000),
|
||||
title: "寒冬腊月时分,洁白雪花纷纷扬扬地覆盖着整个世界,感受冬日的宁静与美好",
|
||||
image: "https://unix.cool-js.com/images/demo/3.jpg"
|
||||
},
|
||||
{
|
||||
id: id++,
|
||||
image: "https://unix.cool-js.com/images/demo/4.jpg",
|
||||
isAd: true
|
||||
},
|
||||
{
|
||||
id: id++,
|
||||
likeCount: random(100, 1000),
|
||||
title: "都市夜景霓虹闪烁,五彩斑斓光芒照亮城市营造梦幻般景象",
|
||||
image: "https://unix.cool-js.com/images/demo/5.jpg"
|
||||
},
|
||||
{
|
||||
id: id++,
|
||||
likeCount: random(100, 1000),
|
||||
title: "云雾缭绕的山间风光如诗如画让人心旷神怡,微风轻抚树梢带来阵阵清香,鸟儿在林间自由歌唱",
|
||||
image: "https://unix.cool-js.com/images/demo/6.jpg"
|
||||
},
|
||||
{
|
||||
id: id++,
|
||||
likeCount: random(100, 1000),
|
||||
title: "古老建筑与现代摩天大楼交相辉映,传统与现代完美融合创造独特城市景观",
|
||||
image: "https://unix.cool-js.com/images/demo/7.jpg"
|
||||
},
|
||||
{
|
||||
id: id++,
|
||||
likeCount: random(100, 1000),
|
||||
title: "广袤田野绿意盎然风光无限,金黄麦浪在微风中轻柔摇曳,农家炊烟袅袅升起",
|
||||
image: "https://unix.cool-js.com/images/demo/8.jpg"
|
||||
},
|
||||
{
|
||||
id: id++,
|
||||
likeCount: random(100, 1000),
|
||||
title: "璀璨星空下银河横跨天际,繁星闪烁神秘光芒营造浪漫夜空美景",
|
||||
image: "https://unix.cool-js.com/images/demo/9.jpg"
|
||||
},
|
||||
{
|
||||
id: id++,
|
||||
likeCount: random(100, 1000),
|
||||
title: "雄伟瀑布从高耸悬崖飞流直下激起千层浪花,彩虹在水雾中若隐若现如梦如幻",
|
||||
image: "https://unix.cool-js.com/images/demo/10.jpg"
|
||||
}
|
||||
];
|
||||
|
||||
waterfallRef.value!.append(items);
|
||||
}
|
||||
|
||||
function del(id: number) {
|
||||
waterfallRef.value!.remove(id);
|
||||
}
|
||||
|
||||
onReachBottom(() => {
|
||||
if (loading.value) return;
|
||||
|
||||
loading.value = true;
|
||||
|
||||
setTimeout(() => {
|
||||
refresh();
|
||||
loading.value = false;
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
refresh();
|
||||
});
|
||||
</script>
|
||||
206
cool-unix/pages/demo/feedback/action-sheet.uvue
Normal file
206
cool-unix/pages/demo/feedback/action-sheet.uvue
Normal file
@@ -0,0 +1,206 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-button @tap="openActionSheet">{{ t("打开") }}</cl-button>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('带图标')">
|
||||
<cl-button @tap="openActionSheet2">{{ t("打开") }}</cl-button>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('带标题、描述')">
|
||||
<cl-button @tap="openActionSheet3">{{ t("打开") }}</cl-button>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('无法点击遮罩关闭')">
|
||||
<cl-button @tap="openActionSheet4">{{ t("打开") }}</cl-button>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('不需要取消按钮')">
|
||||
<cl-button @tap="openActionSheet5">{{ t("打开") }}</cl-button>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('插槽用法')">
|
||||
<cl-button @tap="openActionSheet6">{{ t("打开") }}</cl-button>
|
||||
</demo-item>
|
||||
</view>
|
||||
|
||||
<cl-action-sheet ref="actionSheetRef"> </cl-action-sheet>
|
||||
<cl-action-sheet ref="actionSheetRef2"> </cl-action-sheet>
|
||||
<cl-action-sheet ref="actionSheetRef3"> </cl-action-sheet>
|
||||
<cl-action-sheet ref="actionSheetRef4"> </cl-action-sheet>
|
||||
<cl-action-sheet ref="actionSheetRef5"> </cl-action-sheet>
|
||||
<cl-action-sheet
|
||||
ref="actionSheetRef6"
|
||||
:pt="{
|
||||
list: {
|
||||
className: 'flex-row mx-[-10rpx]'
|
||||
},
|
||||
item: {
|
||||
className: 'flex-1 mx-[10rpx] !rounded-xl'
|
||||
}
|
||||
}"
|
||||
>
|
||||
<template #prepend>
|
||||
<view class="px-3 mb-3">
|
||||
<cl-text>开通会员享受更多特权和服务,包括无广告体验、专属客服等</cl-text>
|
||||
</view>
|
||||
</template>
|
||||
<template #append>
|
||||
<view class="pb-5 pt-2 px-3 flex flex-row items-center">
|
||||
<cl-checkbox v-model="agree">请阅读并同意</cl-checkbox>
|
||||
<cl-text color="primary">《会员服务协议》</cl-text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<template #item="{ item }">
|
||||
<view class="flex flex-col justify-center items-center">
|
||||
<cl-icon :name="item.icon" :size="46"></cl-icon>
|
||||
<cl-text :pt="{ className: 'text-sm mt-1' }">{{ item.label }}</cl-text>
|
||||
</view>
|
||||
</template>
|
||||
</cl-action-sheet>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { ref } from "vue";
|
||||
import { useUi, type ClActionSheetOptions } from "@/uni_modules/cool-ui";
|
||||
|
||||
const ui = useUi();
|
||||
|
||||
const actionSheetRef = ref<ClActionSheetComponentPublicInstance | null>(null);
|
||||
|
||||
function openActionSheet() {
|
||||
actionSheetRef.value!.open({
|
||||
list: [
|
||||
{
|
||||
label: t("反馈")
|
||||
}
|
||||
]
|
||||
} as ClActionSheetOptions);
|
||||
}
|
||||
|
||||
const actionSheetRef2 = ref<ClActionSheetComponentPublicInstance | null>(null);
|
||||
|
||||
function openActionSheet2() {
|
||||
actionSheetRef.value!.open({
|
||||
list: [
|
||||
{
|
||||
label: t("反馈"),
|
||||
icon: "error-warning-line"
|
||||
}
|
||||
]
|
||||
} as ClActionSheetOptions);
|
||||
}
|
||||
|
||||
const actionSheetRef3 = ref<ClActionSheetComponentPublicInstance | null>(null);
|
||||
|
||||
function openActionSheet3() {
|
||||
actionSheetRef.value!.open({
|
||||
title: t("提示"),
|
||||
description: t("删除好友会同时删除所有聊天记录"),
|
||||
list: [
|
||||
{
|
||||
label: t("删除好友"),
|
||||
color: "error",
|
||||
callback() {
|
||||
ui.showConfirm({
|
||||
title: t("提示"),
|
||||
message: t("确定要删除好友吗?"),
|
||||
callback(action) {
|
||||
if (action == "confirm") {
|
||||
ui.showToast({
|
||||
message: t("删除成功")
|
||||
});
|
||||
}
|
||||
|
||||
actionSheetRef.value!.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
]
|
||||
} as ClActionSheetOptions);
|
||||
}
|
||||
|
||||
const actionSheetRef4 = ref<ClActionSheetComponentPublicInstance | null>(null);
|
||||
|
||||
function openActionSheet4() {
|
||||
actionSheetRef.value!.open({
|
||||
maskClosable: false,
|
||||
description: t("无法点击遮罩关闭"),
|
||||
list: []
|
||||
} as ClActionSheetOptions);
|
||||
}
|
||||
|
||||
const actionSheetRef5 = ref<ClActionSheetComponentPublicInstance | null>(null);
|
||||
|
||||
function openActionSheet5() {
|
||||
actionSheetRef.value!.open({
|
||||
showCancel: false,
|
||||
list: [
|
||||
{
|
||||
label: t("点我关闭"),
|
||||
callback() {
|
||||
ui.showConfirm({
|
||||
title: t("提示"),
|
||||
message: t("确定要关闭吗?"),
|
||||
callback(action) {
|
||||
if (action == "confirm") {
|
||||
actionSheetRef.value!.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
]
|
||||
} as ClActionSheetOptions);
|
||||
}
|
||||
|
||||
const agree = ref(false);
|
||||
|
||||
const actionSheetRef6 = ref<ClActionSheetComponentPublicInstance | null>(null);
|
||||
|
||||
function openActionSheet6() {
|
||||
function done() {
|
||||
if (!agree.value) {
|
||||
ui.showToast({
|
||||
message: "请阅读并同意《会员服务协议》"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
ui.showToast({
|
||||
message: t("支付成功")
|
||||
});
|
||||
|
||||
agree.value = false;
|
||||
|
||||
actionSheetRef6.value!.close();
|
||||
}
|
||||
|
||||
actionSheetRef6.value!.open({
|
||||
showCancel: false,
|
||||
list: [
|
||||
{
|
||||
label: t("支付宝"),
|
||||
icon: "alipay-line",
|
||||
callback() {
|
||||
done();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t("微信"),
|
||||
icon: "wechat-line",
|
||||
callback() {
|
||||
done();
|
||||
}
|
||||
}
|
||||
]
|
||||
} as ClActionSheetOptions);
|
||||
}
|
||||
</script>
|
||||
94
cool-unix/pages/demo/feedback/confirm.uvue
Normal file
94
cool-unix/pages/demo/feedback/confirm.uvue
Normal file
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('自定义')">
|
||||
<cl-button @tap="openPopup">{{ t("打开弹窗") }}</cl-button>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('隐藏取消按钮')">
|
||||
<cl-button @tap="openPopup2">{{ t("打开弹窗") }}</cl-button>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义文本')">
|
||||
<cl-button @tap="openPopup3">{{ t("打开弹窗") }}</cl-button>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('关闭前钩子')">
|
||||
<cl-button @tap="openPopup4">{{ t("打开弹窗") }}</cl-button>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('显示时长')">
|
||||
<cl-button @tap="openPopup5">{{ t("打开弹窗") }}</cl-button>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
|
||||
const ui = useUi();
|
||||
|
||||
function openPopup() {
|
||||
ui.showConfirm({
|
||||
title: t("提示"),
|
||||
message: t("确定要删除吗?"),
|
||||
callback(action) {
|
||||
if (action == "confirm") {
|
||||
ui.showToast({
|
||||
message: t("确定")
|
||||
});
|
||||
} else {
|
||||
ui.showToast({
|
||||
message: t("取消")
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function openPopup2() {
|
||||
ui.showConfirm({
|
||||
title: t("提示"),
|
||||
message: t("确定要删除吗?"),
|
||||
showCancel: false
|
||||
});
|
||||
}
|
||||
|
||||
function openPopup3() {
|
||||
ui.showConfirm({
|
||||
title: t("提示"),
|
||||
message: t("确定要删除吗?"),
|
||||
cancelText: t("关闭"),
|
||||
confirmText: t("下一步")
|
||||
});
|
||||
}
|
||||
|
||||
function openPopup4() {
|
||||
ui.showConfirm({
|
||||
title: t("提示"),
|
||||
message: t("确定要删除吗?"),
|
||||
beforeClose: (action, { close, showLoading, hideLoading }) => {
|
||||
if (action == "confirm") {
|
||||
showLoading();
|
||||
|
||||
setTimeout(() => {
|
||||
close();
|
||||
}, 1000);
|
||||
} else {
|
||||
close();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function openPopup5() {
|
||||
ui.showConfirm({
|
||||
title: t("提示"),
|
||||
message: t("确定要删除吗?3秒后自动关闭"),
|
||||
duration: 3000
|
||||
});
|
||||
}
|
||||
</script>
|
||||
100
cool-unix/pages/demo/feedback/popup.uvue
Normal file
100
cool-unix/pages/demo/feedback/popup.uvue
Normal file
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('自定义')">
|
||||
<cl-button @tap="openPopup">{{ t("打开弹窗") }}</cl-button>
|
||||
|
||||
<cl-list border class="mt-3">
|
||||
<view class="w-full p-2">
|
||||
<cl-tabs
|
||||
v-model="direction"
|
||||
:list="directionList"
|
||||
show-slider
|
||||
:height="66"
|
||||
></cl-tabs>
|
||||
</view>
|
||||
|
||||
<cl-list-item :label="t('设置宽度 80%')">
|
||||
<cl-switch v-model="isWidth"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('无头')">
|
||||
<cl-switch v-model="unShowHeader"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('自定义样式')">
|
||||
<cl-switch v-model="isCustom"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
</view>
|
||||
|
||||
<cl-popup
|
||||
v-model="visible"
|
||||
:title="t('标题')"
|
||||
:direction="direction"
|
||||
:size="size"
|
||||
:show-header="!unShowHeader"
|
||||
:pt="{
|
||||
className: parseClass([[isCustom, '!p-3']]),
|
||||
inner: {
|
||||
className: parseClass([[isCustom, '!rounded-2xl']])
|
||||
}
|
||||
}"
|
||||
>
|
||||
<view
|
||||
class="p-3"
|
||||
:class="{
|
||||
'pt-0': !unShowHeader
|
||||
}"
|
||||
>
|
||||
<cl-image
|
||||
src="https://unix.cool-js.com/images/demo/bg1.png"
|
||||
class="mb-3"
|
||||
height="auto"
|
||||
width="100%"
|
||||
mode="widthFix"
|
||||
></cl-image>
|
||||
|
||||
<cl-text
|
||||
>春江花月夜, 花草复青青。 江水流不尽, 月光照无情。 夜来风雨急, 愁思满心头。
|
||||
何时再相见, 共赏月明楼。
|
||||
</cl-text>
|
||||
</view>
|
||||
</cl-popup>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { computed, ref } from "vue";
|
||||
import type { ClPopupDirection, ClTabsItem } from "@/uni_modules/cool-ui";
|
||||
import { parseClass } from "@/cool";
|
||||
|
||||
const visible = ref(false);
|
||||
const isWidth = ref(true);
|
||||
const unShowHeader = ref(false);
|
||||
const isCustom = ref(false);
|
||||
|
||||
const direction = ref<ClPopupDirection>("bottom");
|
||||
const directionList = ref<ClTabsItem[]>([
|
||||
{ label: t("底部"), value: "bottom" },
|
||||
{ label: t("顶部"), value: "top" },
|
||||
{ label: t("左侧"), value: "left" },
|
||||
{ label: t("右侧"), value: "right" },
|
||||
{ label: t("中间"), value: "center" }
|
||||
]);
|
||||
|
||||
const size = computed(() => {
|
||||
if (direction.value == "left" || direction.value == "right" || direction.value == "center") {
|
||||
return isWidth.value ? "80%" : "";
|
||||
}
|
||||
|
||||
return "";
|
||||
});
|
||||
|
||||
function openPopup() {
|
||||
visible.value = true;
|
||||
}
|
||||
</script>
|
||||
93
cool-unix/pages/demo/feedback/toast.uvue
Normal file
93
cool-unix/pages/demo/feedback/toast.uvue
Normal file
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-button @tap="open()">{{ t("打开") }}</cl-button>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('不同位置')">
|
||||
<view class="flex flex-row overflow-visible">
|
||||
<cl-button type="light" @tap="open('top')">{{ t("顶部") }}</cl-button>
|
||||
<cl-button type="light" @tap="open('center')">{{ t("中间") }}</cl-button>
|
||||
<cl-button type="light" @tap="open('bottom')">{{ t("底部") }}</cl-button>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('不同类型')">
|
||||
<view class="flex flex-row flex-wrap mb-2 overflow-visible">
|
||||
<cl-button type="light" @tap="openType('success')">{{ t("成功") }}</cl-button>
|
||||
<cl-button type="light" @tap="openType('error')">{{ t("失败") }}</cl-button>
|
||||
<cl-button type="light" @tap="openType('warn')">{{ t("警告") }}</cl-button>
|
||||
<cl-button type="light" @tap="openType('question')">{{ t("问题") }}</cl-button>
|
||||
<cl-button type="light" @tap="openType('disabled')">{{ t("禁用") }}</cl-button>
|
||||
</view>
|
||||
|
||||
<view class="flex flex-row flex-wrap overflow-visible">
|
||||
<cl-button type="light" @tap="openType('stop')">{{ t("停止") }}</cl-button>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义图标')">
|
||||
<view class="flex flex-row overflow-visible">
|
||||
<cl-button
|
||||
type="light"
|
||||
icon="star-line"
|
||||
@tap="openIcon('star-line')"
|
||||
></cl-button>
|
||||
|
||||
<cl-button
|
||||
type="light"
|
||||
icon="mail-line"
|
||||
@tap="openIcon('mail-line')"
|
||||
></cl-button>
|
||||
|
||||
<cl-button
|
||||
type="light"
|
||||
icon="file-line"
|
||||
@tap="openIcon('file-line')"
|
||||
></cl-button>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('只存在一个')">
|
||||
<cl-button @tap="openClear()">{{ t("打开") }}</cl-button>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useUi, type ClToastType } from "@/uni_modules/cool-ui";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { t } from "@/locale";
|
||||
|
||||
const ui = useUi();
|
||||
|
||||
function open(position: "top" | "center" | "bottom" = "center") {
|
||||
ui.showToast({
|
||||
message: t("不同位置提示"),
|
||||
position: position
|
||||
});
|
||||
}
|
||||
|
||||
function openType(type: ClToastType) {
|
||||
ui.showToast({
|
||||
message: t("不同类型提示"),
|
||||
type
|
||||
});
|
||||
}
|
||||
|
||||
function openIcon(icon: string) {
|
||||
ui.showToast({
|
||||
message: t("带图标提示"),
|
||||
icon
|
||||
});
|
||||
}
|
||||
|
||||
function openClear() {
|
||||
ui.showToast({
|
||||
message: t("移除其他已存在的提示"),
|
||||
clear: true
|
||||
});
|
||||
}
|
||||
</script>
|
||||
206
cool-unix/pages/demo/form/calendar.uvue
Normal file
206
cool-unix/pages/demo/form/calendar.uvue
Normal file
@@ -0,0 +1,206 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('选择器')">
|
||||
<cl-calendar-select v-model="date"></cl-calendar-select>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('多选')">
|
||||
<cl-calendar-select v-model:date="dateArr" mode="multiple"></cl-calendar-select>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('范围选')">
|
||||
<cl-calendar-select v-model:date="dateRange" mode="range"></cl-calendar-select>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('开始 / 结束')">
|
||||
<cl-calendar-select
|
||||
v-model:date="dateRange3"
|
||||
mode="range"
|
||||
:start="startDate"
|
||||
:end="endDate"
|
||||
></cl-calendar-select>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('禁用部分日期')">
|
||||
<cl-calendar-select v-model="date" :date-config="dateConfig"></cl-calendar-select>
|
||||
</demo-item>
|
||||
|
||||
<!-- <demo-item :label="t('日历长列表')">
|
||||
<cl-button>{{ t("打开日历长列表") }}</cl-button>
|
||||
</demo-item> -->
|
||||
|
||||
<demo-item :label="t('日历面板')">
|
||||
<cl-calendar
|
||||
v-model:date="dateRange2"
|
||||
mode="range"
|
||||
:month="10"
|
||||
:show-header="isShowHeader"
|
||||
:show-weeks="isShowWeeks"
|
||||
:show-other-month="isShowOtherMonth"
|
||||
:date-config="dateConfig2"
|
||||
@change="onChange"
|
||||
></cl-calendar>
|
||||
|
||||
<cl-list
|
||||
border
|
||||
:pt="{
|
||||
className: 'mt-5'
|
||||
}"
|
||||
>
|
||||
<cl-list-item :label="t('自定义文案和颜色')">
|
||||
<cl-switch v-model="isCustomDateConfig"></cl-switch>
|
||||
</cl-list-item>
|
||||
<cl-list-item :label="t('显示头')">
|
||||
<cl-switch v-model="isShowHeader"></cl-switch>
|
||||
</cl-list-item>
|
||||
<cl-list-item :label="t('显示星期')">
|
||||
<cl-switch v-model="isShowWeeks"></cl-switch>
|
||||
</cl-list-item>
|
||||
<cl-list-item :label="t('显示其他月份')">
|
||||
<cl-switch v-model="isShowOtherMonth"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from "vue";
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { dayUts, first, last } from "@/cool";
|
||||
import type { ClCalendarDateConfig } from "@/uni_modules/cool-ui";
|
||||
|
||||
const date = ref<string | null>(dayUts().format("YYYY-MM-DD"));
|
||||
|
||||
const dateArr = ref<string[]>([
|
||||
dayUts().format("YYYY-MM-DD"),
|
||||
dayUts().add(1, "day").format("YYYY-MM-DD")
|
||||
]);
|
||||
|
||||
const dateRange = ref<string[]>([
|
||||
dayUts().format("YYYY-MM-DD"),
|
||||
dayUts().add(10, "day").format("YYYY-MM-DD")
|
||||
]);
|
||||
|
||||
const dateConfig = ref<ClCalendarDateConfig[]>([
|
||||
{
|
||||
date: dayUts().add(1, "day").format("YYYY-MM-DD"),
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
date: dayUts().add(2, "day").format("YYYY-MM-DD"),
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
date: dayUts().add(3, "day").format("YYYY-MM-DD"),
|
||||
disabled: true
|
||||
}
|
||||
]);
|
||||
|
||||
const isShowHeader = ref(true);
|
||||
const isShowWeeks = ref(true);
|
||||
const isShowOtherMonth = ref(true);
|
||||
const isCustomDateConfig = ref(true);
|
||||
|
||||
const dateRange2 = ref<string[]>([]);
|
||||
|
||||
const dateConfig2 = computed(() => {
|
||||
const dates = (
|
||||
isCustomDateConfig.value
|
||||
? [
|
||||
{
|
||||
date: "2025-10-01",
|
||||
topText: "国庆节",
|
||||
bottomText: "¥958",
|
||||
color: "red"
|
||||
},
|
||||
{
|
||||
date: "2025-10-02",
|
||||
topText: "休",
|
||||
bottomText: "¥613",
|
||||
color: "red"
|
||||
},
|
||||
{
|
||||
date: "2025-10-03",
|
||||
topText: "休",
|
||||
bottomText: "¥613",
|
||||
color: "red"
|
||||
},
|
||||
{
|
||||
date: "2025-10-04",
|
||||
topText: "休",
|
||||
bottomText: "¥613",
|
||||
color: "red"
|
||||
},
|
||||
{
|
||||
date: "2025-10-05",
|
||||
topText: "休",
|
||||
bottomText: "¥613",
|
||||
color: "red"
|
||||
},
|
||||
{
|
||||
date: "2025-10-06",
|
||||
topText: "休",
|
||||
bottomText: "¥613",
|
||||
color: "red"
|
||||
},
|
||||
{
|
||||
date: "2025-10-07",
|
||||
topText: "休",
|
||||
bottomText: "¥613",
|
||||
color: "red"
|
||||
},
|
||||
{
|
||||
date: "2025-10-08",
|
||||
topText: "休",
|
||||
bottomText: "¥613",
|
||||
color: "red"
|
||||
}
|
||||
]
|
||||
: []
|
||||
) as ClCalendarDateConfig[];
|
||||
|
||||
const startDate = first(dateRange2.value);
|
||||
const endDate = last(dateRange2.value);
|
||||
|
||||
if (startDate != null) {
|
||||
const item = dates.find((e) => e.date == startDate);
|
||||
|
||||
if (item == null) {
|
||||
dates.push({
|
||||
date: startDate,
|
||||
bottomText: "入住"
|
||||
} as ClCalendarDateConfig);
|
||||
} else {
|
||||
item.bottomText = "入住";
|
||||
}
|
||||
}
|
||||
|
||||
if (endDate != null && dateRange2.value.length > 1) {
|
||||
const item = dates.find((e) => e.date == endDate);
|
||||
|
||||
if (item == null) {
|
||||
dates.push({
|
||||
date: endDate,
|
||||
bottomText: "离店"
|
||||
} as ClCalendarDateConfig);
|
||||
} else {
|
||||
item.bottomText = "离店";
|
||||
}
|
||||
}
|
||||
|
||||
return dates;
|
||||
});
|
||||
|
||||
const dateRange3 = ref<string[]>([]);
|
||||
|
||||
const startDate = dayUts().format("YYYY-MM-DD");
|
||||
const endDate = dayUts().add(10, "day").format("YYYY-MM-DD");
|
||||
|
||||
function onChange(date: string[]) {
|
||||
console.log("日期变化:", date);
|
||||
}
|
||||
</script>
|
||||
509
cool-unix/pages/demo/form/cascader.uvue
Normal file
509
cool-unix/pages/demo/form/cascader.uvue
Normal file
@@ -0,0 +1,509 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-cascader v-model="form.cascader1" :options="options"></cl-cascader>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('带索引、地区选择')">
|
||||
<cl-cascader v-model="form.cascader2" :options="options2"></cl-cascader>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义')">
|
||||
<cl-cascader
|
||||
v-model="form.cascader3"
|
||||
:options="options"
|
||||
:disabled="isDisabled"
|
||||
:text-separator="isSeparator ? ' / ' : ' - '"
|
||||
:height="isHeight ? 500 : 800"
|
||||
></cl-cascader>
|
||||
|
||||
<cl-list
|
||||
border
|
||||
:pt="{
|
||||
className: 'mt-3'
|
||||
}"
|
||||
>
|
||||
<cl-list-item :label="t('换个分隔符')">
|
||||
<cl-switch v-model="isSeparator"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('列表高度小一点')">
|
||||
<cl-switch v-model="isHeight"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('禁用')">
|
||||
<cl-switch v-model="isDisabled"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from "vue";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { useCascader, type ClCascaderOption } from "@/uni_modules/cool-ui";
|
||||
import { t } from "@/locale";
|
||||
import pca from "@/data/pca.json";
|
||||
|
||||
type Form = {
|
||||
cascader1: string[];
|
||||
cascader2: string[];
|
||||
cascader3: string[];
|
||||
};
|
||||
|
||||
const form = reactive<Form>({
|
||||
cascader1: [],
|
||||
cascader2: [],
|
||||
cascader3: []
|
||||
});
|
||||
|
||||
const isDisabled = ref(false);
|
||||
const isSeparator = ref(false);
|
||||
const isHeight = ref(false);
|
||||
|
||||
const options = ref<ClCascaderOption[]>([
|
||||
{
|
||||
label: "电子产品",
|
||||
value: "1",
|
||||
children: [
|
||||
{
|
||||
label: "手机",
|
||||
value: "1-1",
|
||||
children: [
|
||||
{
|
||||
label: "苹果",
|
||||
value: "1-1-1",
|
||||
children: [
|
||||
{
|
||||
label: "iPhone 15 Pro Max",
|
||||
value: "1-1-1-1"
|
||||
},
|
||||
{
|
||||
label: "iPhone 15 Pro",
|
||||
value: "1-1-1-2"
|
||||
},
|
||||
{
|
||||
label: "iPhone 15",
|
||||
value: "1-1-1-3"
|
||||
},
|
||||
{
|
||||
label: "iPhone 14 Pro Max",
|
||||
value: "1-1-1-4"
|
||||
},
|
||||
{
|
||||
label: "iPhone 14",
|
||||
value: "1-1-1-5"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "华为",
|
||||
value: "1-1-2",
|
||||
children: [
|
||||
{
|
||||
label: "Mate 60 Pro+",
|
||||
value: "1-1-2-1"
|
||||
},
|
||||
{
|
||||
label: "Mate 60 Pro",
|
||||
value: "1-1-2-2"
|
||||
},
|
||||
{
|
||||
label: "Mate 60",
|
||||
value: "1-1-2-3"
|
||||
},
|
||||
{
|
||||
label: "P60 Pro",
|
||||
value: "1-1-2-4"
|
||||
},
|
||||
{
|
||||
label: "P60",
|
||||
value: "1-1-2-5"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "小米",
|
||||
value: "1-1-3",
|
||||
children: [
|
||||
{
|
||||
label: "小米14 Pro",
|
||||
value: "1-1-3-1"
|
||||
},
|
||||
{
|
||||
label: "小米14",
|
||||
value: "1-1-3-2"
|
||||
},
|
||||
{
|
||||
label: "Redmi K70 Pro",
|
||||
value: "1-1-3-3"
|
||||
},
|
||||
{
|
||||
label: "Redmi K70",
|
||||
value: "1-1-3-4"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "电脑",
|
||||
value: "1-2",
|
||||
children: [
|
||||
{
|
||||
label: "笔记本",
|
||||
value: "1-2-1",
|
||||
children: [
|
||||
{
|
||||
label: "MacBook Pro 16",
|
||||
value: "1-2-1-1"
|
||||
},
|
||||
{
|
||||
label: "MacBook Pro 14",
|
||||
value: "1-2-1-2"
|
||||
},
|
||||
{
|
||||
label: "MacBook Air 15",
|
||||
value: "1-2-1-3"
|
||||
},
|
||||
{
|
||||
label: "ThinkPad X1",
|
||||
value: "1-2-1-4"
|
||||
},
|
||||
{
|
||||
label: "ROG 魔霸新锐",
|
||||
value: "1-2-1-5"
|
||||
},
|
||||
{
|
||||
label: "拯救者 Y9000P",
|
||||
value: "1-2-1-6"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "台式机",
|
||||
value: "1-2-2",
|
||||
children: [
|
||||
{
|
||||
label: "iMac 24寸",
|
||||
value: "1-2-2-1"
|
||||
},
|
||||
{
|
||||
label: "Mac Studio",
|
||||
value: "1-2-2-2"
|
||||
},
|
||||
{
|
||||
label: "Mac Pro",
|
||||
value: "1-2-2-3"
|
||||
},
|
||||
{
|
||||
label: "外星人",
|
||||
value: "1-2-2-4"
|
||||
},
|
||||
{
|
||||
label: "惠普暗影精灵",
|
||||
value: "1-2-2-5"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "平板",
|
||||
value: "1-3",
|
||||
children: [
|
||||
{
|
||||
label: "iPad",
|
||||
value: "1-3-1",
|
||||
children: [
|
||||
{
|
||||
label: "iPad Pro 12.9",
|
||||
value: "1-3-1-1"
|
||||
},
|
||||
{
|
||||
label: "iPad Pro 11",
|
||||
value: "1-3-1-2"
|
||||
},
|
||||
{
|
||||
label: "iPad Air",
|
||||
value: "1-3-1-3"
|
||||
},
|
||||
{
|
||||
label: "iPad mini",
|
||||
value: "1-3-1-4"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "安卓平板",
|
||||
value: "1-3-2",
|
||||
children: [
|
||||
{
|
||||
label: "小米平板6 Pro",
|
||||
value: "1-3-2-1"
|
||||
},
|
||||
{
|
||||
label: "华为MatePad Pro",
|
||||
value: "1-3-2-2"
|
||||
},
|
||||
{
|
||||
label: "三星Galaxy Tab S9",
|
||||
value: "1-3-2-3"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "服装",
|
||||
value: "2",
|
||||
children: [
|
||||
{
|
||||
label: "男装",
|
||||
value: "2-1",
|
||||
children: [
|
||||
{
|
||||
label: "上衣",
|
||||
value: "2-1-1",
|
||||
children: [
|
||||
{
|
||||
label: "短袖T恤",
|
||||
value: "2-1-1-1"
|
||||
},
|
||||
{
|
||||
label: "长袖T恤",
|
||||
value: "2-1-1-2"
|
||||
},
|
||||
{
|
||||
label: "衬衫",
|
||||
value: "2-1-1-3"
|
||||
},
|
||||
{
|
||||
label: "卫衣",
|
||||
value: "2-1-1-4"
|
||||
},
|
||||
{
|
||||
label: "夹克",
|
||||
value: "2-1-1-5"
|
||||
},
|
||||
{
|
||||
label: "毛衣",
|
||||
value: "2-1-1-6"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "裤装",
|
||||
value: "2-1-2",
|
||||
children: [
|
||||
{
|
||||
label: "牛仔裤",
|
||||
value: "2-1-2-1"
|
||||
},
|
||||
{
|
||||
label: "休闲裤",
|
||||
value: "2-1-2-2"
|
||||
},
|
||||
{
|
||||
label: "运动裤",
|
||||
value: "2-1-2-3"
|
||||
},
|
||||
{
|
||||
label: "西裤",
|
||||
value: "2-1-2-4"
|
||||
},
|
||||
{
|
||||
label: "短裤",
|
||||
value: "2-1-2-5"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "外套",
|
||||
value: "2-1-3",
|
||||
children: [
|
||||
{
|
||||
label: "羽绒服",
|
||||
value: "2-1-3-1"
|
||||
},
|
||||
{
|
||||
label: "大衣",
|
||||
value: "2-1-3-2"
|
||||
},
|
||||
{
|
||||
label: "夹克",
|
||||
value: "2-1-3-3"
|
||||
},
|
||||
{
|
||||
label: "西装",
|
||||
value: "2-1-3-4"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "女装",
|
||||
value: "2-2",
|
||||
children: [
|
||||
{
|
||||
label: "裙装",
|
||||
value: "2-2-1",
|
||||
children: [
|
||||
{
|
||||
label: "连衣裙",
|
||||
value: "2-2-1-1"
|
||||
},
|
||||
{
|
||||
label: "半身裙",
|
||||
value: "2-2-1-2"
|
||||
},
|
||||
{
|
||||
label: "A字裙",
|
||||
value: "2-2-1-3"
|
||||
},
|
||||
{
|
||||
label: "包臀裙",
|
||||
value: "2-2-1-4"
|
||||
},
|
||||
{
|
||||
label: "百褶裙",
|
||||
value: "2-2-1-5"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "上装",
|
||||
value: "2-2-2",
|
||||
children: [
|
||||
{
|
||||
label: "衬衫",
|
||||
value: "2-2-2-1"
|
||||
},
|
||||
{
|
||||
label: "T恤",
|
||||
value: "2-2-2-2"
|
||||
},
|
||||
{
|
||||
label: "毛衣",
|
||||
value: "2-2-2-3"
|
||||
},
|
||||
{
|
||||
label: "卫衣",
|
||||
value: "2-2-2-4"
|
||||
},
|
||||
{
|
||||
label: "雪纺衫",
|
||||
value: "2-2-2-5"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "外套",
|
||||
value: "2-2-3",
|
||||
children: [
|
||||
{
|
||||
label: "风衣",
|
||||
value: "2-2-3-1"
|
||||
},
|
||||
{
|
||||
label: "羽绒服",
|
||||
value: "2-2-3-2"
|
||||
},
|
||||
{
|
||||
label: "大衣",
|
||||
value: "2-2-3-3"
|
||||
},
|
||||
{
|
||||
label: "西装",
|
||||
value: "2-2-3-4"
|
||||
},
|
||||
{
|
||||
label: "皮衣",
|
||||
value: "2-2-3-5"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "食品",
|
||||
value: "3",
|
||||
children: [
|
||||
{
|
||||
label: "水果",
|
||||
value: "3-1",
|
||||
children: [
|
||||
{
|
||||
label: "苹果",
|
||||
value: "3-1-1"
|
||||
},
|
||||
{
|
||||
label: "香蕉",
|
||||
value: "3-1-2"
|
||||
},
|
||||
{
|
||||
label: "橘子",
|
||||
value: "3-1-3"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "蔬菜",
|
||||
value: "3-2",
|
||||
children: [
|
||||
{
|
||||
label: "西红柿",
|
||||
value: "3-2-1"
|
||||
},
|
||||
{
|
||||
label: "黄瓜",
|
||||
value: "3-2-2"
|
||||
},
|
||||
{
|
||||
label: "胡萝卜",
|
||||
value: "3-2-3"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "饮料",
|
||||
value: "4",
|
||||
children: [
|
||||
{
|
||||
label: "果汁",
|
||||
value: "4-1",
|
||||
children: [
|
||||
{
|
||||
label: "苹果汁",
|
||||
value: "4-1-1"
|
||||
},
|
||||
{
|
||||
label: "橙汁",
|
||||
value: "4-1-2"
|
||||
},
|
||||
{
|
||||
label: "葡萄汁",
|
||||
value: "4-1-3"
|
||||
},
|
||||
{
|
||||
label: "西瓜汁",
|
||||
value: "4-1-4"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
const options2 = useCascader(pca);
|
||||
</script>
|
||||
163
cool-unix/pages/demo/form/checkbox.uvue
Normal file
163
cool-unix/pages/demo/form/checkbox.uvue
Normal file
@@ -0,0 +1,163 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<view class="flex flex-row flex-wrap">
|
||||
<cl-checkbox
|
||||
v-model="checked2"
|
||||
v-for="(item, index) in options"
|
||||
:key="index"
|
||||
:value="item.value"
|
||||
:pt="{
|
||||
className: 'mr-5'
|
||||
}"
|
||||
>
|
||||
{{ item.label }}
|
||||
</cl-checkbox>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('单个 true / false')">
|
||||
<view class="flex flex-row items-center">
|
||||
<cl-checkbox v-model="checked4">同意并阅读</cl-checkbox>
|
||||
<cl-text color="primary">《用户协议》</cl-text>
|
||||
|
||||
<view class="flex-1"></view>
|
||||
|
||||
<cl-switch v-model="checked4"></cl-switch>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('纵向排列')">
|
||||
<cl-checkbox
|
||||
v-model="checked"
|
||||
v-for="(item, index) in options"
|
||||
:key="index"
|
||||
:value="item.value"
|
||||
:pt="{
|
||||
className: parseClass([
|
||||
'py-2',
|
||||
[
|
||||
isVerticalCustom,
|
||||
'justify-between border border-solid border-surface-200 rounded-lg p-2 !my-1'
|
||||
]
|
||||
])
|
||||
}"
|
||||
>
|
||||
{{ item.label }}
|
||||
</cl-checkbox>
|
||||
|
||||
<cl-list
|
||||
border
|
||||
:pt="{
|
||||
className: 'mt-2'
|
||||
}"
|
||||
>
|
||||
<cl-list-item :label="t('换个样式')">
|
||||
<cl-switch v-model="isVerticalCustom"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义')">
|
||||
<view class="mb-3 flex flex-row flex-wrap">
|
||||
<cl-checkbox
|
||||
v-model="checked3"
|
||||
v-for="(item, index) in options"
|
||||
:key="index"
|
||||
:value="item.value"
|
||||
:disabled="isDisabled"
|
||||
:show-icon="!isHideIcon"
|
||||
:active-icon="isIcon ? 'heart-fill' : 'checkbox-line'"
|
||||
:inactive-icon="isIcon ? 'heart-line' : 'checkbox-blank-line'"
|
||||
:pt="{
|
||||
className: parseClass([
|
||||
'mr-5',
|
||||
[isCustom, 'bg-surface-100 py-2 px-3 rounded-lg !mr-2 !mb-2'],
|
||||
{
|
||||
'!bg-surface-700': isDark && isCustom
|
||||
}
|
||||
]),
|
||||
icon: {
|
||||
className: parseClass([
|
||||
[
|
||||
isCustom && checked3.includes(item.value as string),
|
||||
'text-red-500'
|
||||
]
|
||||
])
|
||||
},
|
||||
label: {
|
||||
className: parseClass([
|
||||
[
|
||||
isCustom && checked3.includes(item.value as string),
|
||||
'text-red-500'
|
||||
]
|
||||
])
|
||||
}
|
||||
}"
|
||||
>
|
||||
{{ item.label }}
|
||||
</cl-checkbox>
|
||||
</view>
|
||||
|
||||
<cl-list border>
|
||||
<cl-list-item :label="t('换个图标')">
|
||||
<cl-switch v-model="isIcon"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('不显示图标')">
|
||||
<cl-switch v-model="isHideIcon"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('禁用')">
|
||||
<cl-switch v-model="isDisabled"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('其他样式')">
|
||||
<cl-switch v-model="isCustom"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { useUi, type ClCheckboxOption } from "@/uni_modules/cool-ui";
|
||||
import { isDark, parseClass } from "@/cool";
|
||||
import { t } from "@/locale";
|
||||
|
||||
const ui = useUi();
|
||||
|
||||
const isIcon = ref(false);
|
||||
const isCustom = ref(false);
|
||||
const isDisabled = ref(false);
|
||||
const isHideIcon = ref(false);
|
||||
const isVerticalCustom = ref(false);
|
||||
|
||||
const checked = ref<string[]>([]);
|
||||
const checked2 = ref<string[]>(["2"]);
|
||||
const checked3 = ref<string[]>(["2", "3"]);
|
||||
const checked4 = ref(false);
|
||||
|
||||
const options = ref<ClCheckboxOption[]>([
|
||||
{
|
||||
label: "Vue",
|
||||
value: "1"
|
||||
},
|
||||
{
|
||||
label: "React",
|
||||
value: "2"
|
||||
},
|
||||
{
|
||||
label: "Angular",
|
||||
value: "3"
|
||||
},
|
||||
{
|
||||
label: "Svelte",
|
||||
value: "4"
|
||||
}
|
||||
]);
|
||||
</script>
|
||||
381
cool-unix/pages/demo/form/form.uvue
Normal file
381
cool-unix/pages/demo/form/form.uvue
Normal file
@@ -0,0 +1,381 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item>
|
||||
<cl-form
|
||||
:pt="{
|
||||
className: 'p-2 pb-0'
|
||||
}"
|
||||
v-model="formData"
|
||||
ref="formRef"
|
||||
:rules="rules"
|
||||
:disabled="saving"
|
||||
label-position="top"
|
||||
>
|
||||
<cl-form-item prop="avatarUrl">
|
||||
<cl-upload v-model="formData.avatarUrl" test></cl-upload>
|
||||
</cl-form-item>
|
||||
|
||||
<cl-form-item :label="t('用户名')" prop="nickName" required>
|
||||
<cl-input
|
||||
v-model="formData.nickName"
|
||||
:placeholder="t('请输入用户名')"
|
||||
clearable
|
||||
></cl-input>
|
||||
</cl-form-item>
|
||||
|
||||
<cl-form-item :label="t('邮箱')" prop="email">
|
||||
<cl-input
|
||||
v-model="formData.email"
|
||||
:placeholder="t('请输入邮箱地址')"
|
||||
></cl-input>
|
||||
</cl-form-item>
|
||||
|
||||
<cl-form-item :label="t('动态验证')" required prop="contacts">
|
||||
<view
|
||||
class="contacts border border-solid border-surface-200 rounded-xl p-3 dark:!border-surface-700"
|
||||
>
|
||||
<cl-form-item
|
||||
v-for="(item, index) in formData.contacts"
|
||||
:key="index"
|
||||
:label="t('联系人') + ` - ${index + 1}`"
|
||||
:prop="`contacts[${index}].phone`"
|
||||
:rules="
|
||||
[
|
||||
{
|
||||
required: true,
|
||||
message: t('手机号不能为空')
|
||||
}
|
||||
] as ClFormRule[]
|
||||
"
|
||||
required
|
||||
>
|
||||
<view class="flex flex-row items-center">
|
||||
<cl-input
|
||||
:pt="{
|
||||
className: 'flex-1 mr-2'
|
||||
}"
|
||||
v-model="item.phone"
|
||||
:placeholder="t('请输入手机号')"
|
||||
></cl-input>
|
||||
|
||||
<cl-button
|
||||
type="light"
|
||||
icon="subtract-line"
|
||||
@tap="removeContact(index)"
|
||||
></cl-button>
|
||||
</view>
|
||||
</cl-form-item>
|
||||
|
||||
<cl-button icon="add-line" @tap="addContact">{{
|
||||
t("添加联系人")
|
||||
}}</cl-button>
|
||||
</view>
|
||||
</cl-form-item>
|
||||
|
||||
<cl-form-item :label="t('身高')" prop="height" required>
|
||||
<cl-slider v-model="formData.height" :max="220" show-value>
|
||||
<template #value="{ value }">
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'text-center w-[120rpx]'
|
||||
}"
|
||||
>{{ value }} cm</cl-text
|
||||
>
|
||||
</template>
|
||||
</cl-slider>
|
||||
</cl-form-item>
|
||||
|
||||
<cl-form-item :label="t('体重')" prop="weight" required>
|
||||
<cl-slider v-model="formData.weight" :max="150" show-value>
|
||||
<template #value="{ value }">
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'text-center w-[120rpx]'
|
||||
}"
|
||||
>{{ value }} kg</cl-text
|
||||
>
|
||||
</template>
|
||||
</cl-slider>
|
||||
</cl-form-item>
|
||||
|
||||
<cl-form-item :label="t('标签')" prop="tags" required>
|
||||
<view class="flex flex-row flex-wrap">
|
||||
<cl-checkbox
|
||||
v-model="formData.tags"
|
||||
v-for="(item, index) in tagsOptions"
|
||||
:key="index"
|
||||
:value="index"
|
||||
:pt="{
|
||||
className: 'mr-5 mt-2'
|
||||
}"
|
||||
>{{ item.label }}</cl-checkbox
|
||||
>
|
||||
</view>
|
||||
</cl-form-item>
|
||||
|
||||
<cl-form-item :label="t('性别')" prop="gender" required>
|
||||
<cl-select v-model="formData.gender" :options="genderOptions"></cl-select>
|
||||
</cl-form-item>
|
||||
|
||||
<cl-form-item :label="t('所在地区')" prop="pca" required>
|
||||
<cl-cascader v-model="formData.pca" :options="pcaOptions"></cl-cascader>
|
||||
</cl-form-item>
|
||||
|
||||
<cl-form-item :label="t('出生年月')" prop="birthday" required>
|
||||
<cl-select-date v-model="formData.birthday" type="date"></cl-select-date>
|
||||
</cl-form-item>
|
||||
|
||||
<cl-form-item :label="t('个人简介')" prop="description">
|
||||
<cl-textarea
|
||||
v-model="formData.description"
|
||||
:placeholder="t('请输入个人简介')"
|
||||
:maxlength="200"
|
||||
></cl-textarea>
|
||||
</cl-form-item>
|
||||
|
||||
<cl-form-item :label="t('公开状态')">
|
||||
<cl-switch v-model="formData.isPublic"></cl-switch>
|
||||
</cl-form-item>
|
||||
</cl-form>
|
||||
</demo-item>
|
||||
|
||||
<demo-item>
|
||||
<cl-text pre-wrap :pt="{ className: 'text-sm p-2' }">{{
|
||||
JSON.stringify(formData, null, 4)
|
||||
}}</cl-text>
|
||||
</demo-item>
|
||||
</view>
|
||||
|
||||
<cl-footer>
|
||||
<view class="flex flex-row">
|
||||
<cl-button type="info" :pt="{ className: 'flex-1' }" @click="reset">{{
|
||||
t("重置")
|
||||
}}</cl-button>
|
||||
<cl-button
|
||||
type="primary"
|
||||
:loading="saving"
|
||||
:pt="{ className: 'flex-1' }"
|
||||
@click="submit"
|
||||
>{{ t("提交") }}</cl-button
|
||||
>
|
||||
</view>
|
||||
</cl-footer>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, type Ref } from "vue";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import {
|
||||
useCascader,
|
||||
useForm,
|
||||
useUi,
|
||||
type ClFormRule,
|
||||
type ClSelectOption
|
||||
} from "@/uni_modules/cool-ui";
|
||||
import pca from "@/data/pca.json";
|
||||
import { t } from "@/locale";
|
||||
import { dayUts } from "@/cool";
|
||||
|
||||
const ui = useUi();
|
||||
const { formRef, validate, clearValidate } = useForm();
|
||||
|
||||
// 性别选项
|
||||
const genderOptions = [
|
||||
{
|
||||
label: t("未知"),
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
label: t("男"),
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: t("女"),
|
||||
value: 2
|
||||
}
|
||||
] as ClSelectOption[];
|
||||
|
||||
// 标签选项
|
||||
const tagsOptions = [
|
||||
{
|
||||
label: t("篮球"),
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: t("足球"),
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
label: t("羽毛球"),
|
||||
value: 3
|
||||
},
|
||||
{
|
||||
label: t("乒乓球"),
|
||||
value: 4
|
||||
},
|
||||
{
|
||||
label: t("游泳"),
|
||||
value: 5
|
||||
}
|
||||
] as ClSelectOption[];
|
||||
|
||||
// 地区选项
|
||||
const pcaOptions = useCascader(pca);
|
||||
|
||||
type Contact = {
|
||||
phone: string;
|
||||
};
|
||||
|
||||
// 自定义表单数据类型
|
||||
type FormData = {
|
||||
avatarUrl: string;
|
||||
nickName: string;
|
||||
email: string;
|
||||
height: number;
|
||||
weight: number;
|
||||
gender: number;
|
||||
description: string;
|
||||
pca: string[];
|
||||
tags: number[];
|
||||
birthday: string;
|
||||
isPublic: boolean;
|
||||
contacts: Contact[];
|
||||
};
|
||||
|
||||
// 表单数据
|
||||
const formData = ref<FormData>({
|
||||
avatarUrl: "",
|
||||
nickName: "神仙都没用",
|
||||
email: "",
|
||||
height: 180,
|
||||
weight: 70,
|
||||
gender: 0,
|
||||
description: "",
|
||||
pca: [],
|
||||
tags: [1, 2],
|
||||
birthday: "",
|
||||
isPublic: false,
|
||||
contacts: []
|
||||
}) as Ref<FormData>;
|
||||
|
||||
// 表单验证规则
|
||||
const rules = new Map<string, ClFormRule[]>([
|
||||
[
|
||||
"nickName",
|
||||
[
|
||||
{ required: true, message: t("用户名不能为空") },
|
||||
{ min: 3, max: 20, message: t("用户名长度在3-20个字符之间") }
|
||||
]
|
||||
],
|
||||
[
|
||||
"email",
|
||||
[
|
||||
{ required: true, message: t("邮箱不能为空") },
|
||||
{ pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, message: t("邮箱格式不正确") }
|
||||
]
|
||||
],
|
||||
[
|
||||
"height",
|
||||
[
|
||||
{ required: true, message: t("身高不能为空") },
|
||||
{ min: 160, max: 190, message: t("身高在160-190cm之间") }
|
||||
]
|
||||
],
|
||||
[
|
||||
"weight",
|
||||
[
|
||||
{ required: true, message: t("体重不能为空") },
|
||||
{ min: 40, max: 100, message: t("体重在40-100kg之间") }
|
||||
]
|
||||
],
|
||||
[
|
||||
"tags",
|
||||
[
|
||||
{ required: true, message: t("标签不能为空") },
|
||||
{ min: 1, max: 2, message: t("标签最多选择2个") }
|
||||
]
|
||||
],
|
||||
["gender", [{ required: true, message: t("性别不能为空") }]],
|
||||
["pca", [{ required: true, message: t("所在地区不能为空") }]],
|
||||
[
|
||||
"birthday",
|
||||
[
|
||||
{ required: true, message: t("出生年月不能为空") },
|
||||
{
|
||||
validator(value) {
|
||||
if (dayUts(value).isAfter(dayUts("2010-01-01"))) {
|
||||
return t("出生年月不大于2010-01-01");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
[
|
||||
"contacts",
|
||||
[
|
||||
{
|
||||
required: true,
|
||||
message: t("联系人不能为空")
|
||||
}
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
// 是否保存中
|
||||
const saving = ref(false);
|
||||
|
||||
// 重置表单数据
|
||||
function reset() {
|
||||
formData.value.avatarUrl = "";
|
||||
formData.value.nickName = "";
|
||||
formData.value.email = "";
|
||||
formData.value.height = 180;
|
||||
formData.value.weight = 70;
|
||||
formData.value.gender = 0;
|
||||
formData.value.description = "";
|
||||
formData.value.pca = [];
|
||||
formData.value.tags = [];
|
||||
formData.value.birthday = "";
|
||||
formData.value.isPublic = false;
|
||||
formData.value.contacts = [];
|
||||
|
||||
clearValidate();
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
function submit() {
|
||||
validate((valid, errors) => {
|
||||
if (valid) {
|
||||
saving.value = true;
|
||||
|
||||
setTimeout(() => {
|
||||
ui.showToast({
|
||||
message: t("提交成功"),
|
||||
icon: "check-line"
|
||||
});
|
||||
|
||||
saving.value = false;
|
||||
reset();
|
||||
}, 2000);
|
||||
} else {
|
||||
ui.showToast({
|
||||
message: errors[0].message
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addContact() {
|
||||
formData.value.contacts.push({
|
||||
phone: ""
|
||||
});
|
||||
}
|
||||
|
||||
function removeContact(index: number) {
|
||||
formData.value.contacts.splice(index, 1);
|
||||
}
|
||||
</script>
|
||||
93
cool-unix/pages/demo/form/input-number.uvue
Normal file
93
cool-unix/pages/demo/form/input-number.uvue
Normal file
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-input-number></cl-input-number>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义')">
|
||||
<view class="mb-5 flex flex-row justify-center">
|
||||
<cl-input-number
|
||||
v-model="num"
|
||||
:step="isStep ? 10 : 1"
|
||||
:min="isMin ? 10 : 0"
|
||||
:max="isMax ? 50 : 100"
|
||||
:input-type="isDigit ? 'digit' : 'number'"
|
||||
:inputable="isInput"
|
||||
:disabled="isDisabled"
|
||||
:size="isSize ? 60 : 50"
|
||||
:pt="{
|
||||
op: {
|
||||
className: parseClass({
|
||||
'!rounded-full': isCustom
|
||||
})
|
||||
},
|
||||
value: {
|
||||
className: parseClass({
|
||||
'!rounded-full': isCustom
|
||||
})
|
||||
}
|
||||
}"
|
||||
@change="onChange"
|
||||
></cl-input-number>
|
||||
</view>
|
||||
|
||||
<cl-list border>
|
||||
<cl-list-item :label="t('步进为10')">
|
||||
<cl-switch v-model="isStep"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('最小为10')">
|
||||
<cl-switch v-model="isMin"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('最大为50')">
|
||||
<cl-switch v-model="isMax"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('可以小数')">
|
||||
<cl-switch v-model="isDigit"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('可以输入')">
|
||||
<cl-switch v-model="isInput"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('禁用')">
|
||||
<cl-switch v-model="isDisabled"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('大一点')">
|
||||
<cl-switch v-model="isSize"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('自定义样式')">
|
||||
<cl-switch v-model="isCustom"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { parseClass } from "@/cool";
|
||||
import { t } from "@/locale";
|
||||
|
||||
const num = ref(0);
|
||||
|
||||
const isStep = ref(false);
|
||||
const isMin = ref(false);
|
||||
const isMax = ref(false);
|
||||
const isDigit = ref(false);
|
||||
const isInput = ref(true);
|
||||
const isDisabled = ref(false);
|
||||
const isSize = ref(false);
|
||||
const isCustom = ref(false);
|
||||
|
||||
function onChange(value: number) {
|
||||
console.log(value);
|
||||
}
|
||||
</script>
|
||||
73
cool-unix/pages/demo/form/input-otp.uvue
Normal file
73
cool-unix/pages/demo/form/input-otp.uvue
Normal file
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-input-otp @done="toDone"></cl-input-otp>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自动聚焦')">
|
||||
<cl-input-otp autofocus @done="toDone"></cl-input-otp>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义')">
|
||||
<cl-input-otp
|
||||
:length="isLength ? 6 : 4"
|
||||
:disabled="isDisabled"
|
||||
:pt="{
|
||||
item: {
|
||||
className: parseClass({
|
||||
'!bg-sky-100': isCustom,
|
||||
'!border-white': isCustom
|
||||
})
|
||||
},
|
||||
value: {
|
||||
className: parseClass({
|
||||
'!text-sky-700': isCustom
|
||||
})
|
||||
},
|
||||
cursor: {
|
||||
className: parseClass({
|
||||
'!bg-sky-700': isCustom
|
||||
})
|
||||
}
|
||||
}"
|
||||
@done="toDone"
|
||||
></cl-input-otp>
|
||||
|
||||
<cl-list border :pt="{ className: 'mt-5' }">
|
||||
<cl-list-item :label="t('长度为6')">
|
||||
<cl-switch v-model="isLength"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('禁用')">
|
||||
<cl-switch v-model="isDisabled"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('其他样式')">
|
||||
<cl-switch v-model="isCustom"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
import { parseClass } from "@/cool";
|
||||
import { t } from "@/locale";
|
||||
|
||||
const ui = useUi();
|
||||
|
||||
const isLength = ref(true);
|
||||
const isDisabled = ref(false);
|
||||
const isCustom = ref(false);
|
||||
|
||||
function toDone() {
|
||||
ui.showToast({
|
||||
message: "Done"
|
||||
});
|
||||
}
|
||||
</script>
|
||||
136
cool-unix/pages/demo/form/input.uvue
Normal file
136
cool-unix/pages/demo/form/input.uvue
Normal file
@@ -0,0 +1,136 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-input></cl-input>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('数字输入')">
|
||||
<cl-input type="number"></cl-input>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('密码输入')">
|
||||
<cl-input password></cl-input>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('可清除')">
|
||||
<cl-input clearable :pt="{ className: 'mb-2' }"></cl-input>
|
||||
|
||||
<demo-tips>设置 hold-keyboard 属性后,清除内容时输入框将保持聚焦状态</demo-tips>
|
||||
|
||||
<cl-input clearable hold-keyboard></cl-input>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('左右插槽')">
|
||||
<cl-input
|
||||
:pt="{
|
||||
className: '!pr-1 mb-2'
|
||||
}"
|
||||
>
|
||||
<template #append>
|
||||
<cl-button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="send-plane-fill"
|
||||
:pt="{
|
||||
className: 'ml-2'
|
||||
}"
|
||||
@tap="toAlert"
|
||||
></cl-button>
|
||||
</template>
|
||||
</cl-input>
|
||||
|
||||
<cl-input
|
||||
:pt="{
|
||||
className: '!pl-1'
|
||||
}"
|
||||
>
|
||||
<template #prepend>
|
||||
<cl-button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="search-line"
|
||||
:pt="{
|
||||
className: 'mr-2'
|
||||
}"
|
||||
@tap="toAlert"
|
||||
></cl-button>
|
||||
</template>
|
||||
</cl-input>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义')">
|
||||
<cl-input
|
||||
v-model="content"
|
||||
:border="isBorder"
|
||||
:suffix-icon="isRightIcon ? 'text' : ''"
|
||||
:prefix-icon="isLeftIcon ? 'search-line' : ''"
|
||||
:disabled="isDisabled"
|
||||
:pt="{
|
||||
className: parseClass({
|
||||
'!bg-sky-100': isColor,
|
||||
'!border-sky-700': isColor
|
||||
}),
|
||||
inner: {
|
||||
className: parseClass({
|
||||
'!text-sky-700': isColor
|
||||
})
|
||||
},
|
||||
prefixIcon: {
|
||||
className: parseClass({
|
||||
'!text-sky-700': isColor
|
||||
})
|
||||
}
|
||||
}"
|
||||
></cl-input>
|
||||
|
||||
<cl-list border :pt="{ className: 'mt-5' }">
|
||||
<cl-list-item :label="t('边框')">
|
||||
<cl-switch v-model="isBorder"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('左图标')">
|
||||
<cl-switch v-model="isLeftIcon"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('右图标')">
|
||||
<cl-switch v-model="isRightIcon"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('禁用')">
|
||||
<cl-switch v-model="isDisabled"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('其他颜色')">
|
||||
<cl-switch v-model="isColor"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import DemoTips from "../components/tips.uvue";
|
||||
import { parseClass } from "@/cool";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
import { t } from "@/locale";
|
||||
|
||||
const ui = useUi();
|
||||
|
||||
const content = ref("Cool Unix");
|
||||
|
||||
const isBorder = ref(true);
|
||||
const isLeftIcon = ref(true);
|
||||
const isRightIcon = ref(false);
|
||||
const isDisabled = ref(false);
|
||||
const isColor = ref(false);
|
||||
|
||||
function toAlert() {
|
||||
ui.showToast({
|
||||
message: "Hello"
|
||||
});
|
||||
}
|
||||
</script>
|
||||
111
cool-unix/pages/demo/form/keyboard.uvue
Normal file
111
cool-unix/pages/demo/form/keyboard.uvue
Normal file
@@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('数字键盘')">
|
||||
<view class="mb-3 overflow-visible">
|
||||
<cl-input type="number" v-model="content"></cl-input>
|
||||
</view>
|
||||
|
||||
<cl-button @tap="openKeyboardNumber">{{ t("打开键盘") }}</cl-button>
|
||||
|
||||
<cl-list
|
||||
border
|
||||
:pt="{
|
||||
className: 'mt-3'
|
||||
}"
|
||||
>
|
||||
<cl-list-item :label="t('是否显示输入值')">
|
||||
<cl-switch v-model="isShowValue"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('输入即绑定')">
|
||||
<cl-switch v-model="isInputImmediate"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('身份证键盘')">
|
||||
<cl-switch v-model="isIdcard"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('密码键盘')">
|
||||
<cl-button @tap="openKeyboardPassword">{{ t("打开键盘") }}</cl-button>
|
||||
|
||||
<cl-list
|
||||
border
|
||||
:pt="{
|
||||
className: 'mt-3'
|
||||
}"
|
||||
>
|
||||
<cl-list-item :label="t('是否加密')">
|
||||
<cl-switch v-model="isEncrypt"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('车牌号键盘')">
|
||||
<view class="flex mb-3 justify-center flex-row overflow-visible">
|
||||
<cl-input-otp
|
||||
input-type="text"
|
||||
:length="8"
|
||||
:pt="{
|
||||
className: 'w-full',
|
||||
list: {
|
||||
className: 'justify-between'
|
||||
},
|
||||
item: {
|
||||
className: '!h-9 !w-9'
|
||||
},
|
||||
cursor: {
|
||||
className: '!h-3'
|
||||
}
|
||||
}"
|
||||
v-model="carNumber"
|
||||
></cl-input-otp>
|
||||
</view>
|
||||
|
||||
<cl-button @tap="openKeyboardCar">{{ t("打开键盘") }}</cl-button>
|
||||
</demo-item>
|
||||
</view>
|
||||
|
||||
<cl-keyboard-number
|
||||
v-model="content"
|
||||
ref="keyboardNumberRef"
|
||||
:show-value="isShowValue"
|
||||
:input-immediate="isInputImmediate"
|
||||
:type="isIdcard ? 'idcard' : 'number'"
|
||||
></cl-keyboard-number>
|
||||
<cl-keyboard-car v-model="carNumber" ref="keyboardCarRef"></cl-keyboard-car>
|
||||
<cl-keyboard-password ref="keyboardPasswordRef" :encrypt="isEncrypt"></cl-keyboard-password>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { t } from "@/locale";
|
||||
|
||||
const keyboardNumberRef = ref<ClKeyboardNumberComponentPublicInstance | null>(null);
|
||||
const isShowValue = ref(true);
|
||||
const isInputImmediate = ref(false);
|
||||
const isIdcard = ref(false);
|
||||
const content = ref("");
|
||||
|
||||
function openKeyboardNumber() {
|
||||
keyboardNumberRef.value!.open();
|
||||
}
|
||||
|
||||
const keyboardPasswordRef = ref<ClKeyboardPasswordComponentPublicInstance | null>(null);
|
||||
const isEncrypt = ref(false);
|
||||
|
||||
function openKeyboardPassword() {
|
||||
keyboardPasswordRef.value!.open();
|
||||
}
|
||||
|
||||
const keyboardCarRef = ref<ClKeyboardCarComponentPublicInstance | null>(null);
|
||||
const carNumber = ref("");
|
||||
|
||||
function openKeyboardCar() {
|
||||
keyboardCarRef.value!.open();
|
||||
}
|
||||
</script>
|
||||
146
cool-unix/pages/demo/form/radio.uvue
Normal file
146
cool-unix/pages/demo/form/radio.uvue
Normal file
@@ -0,0 +1,146 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<view class="flex flex-row flex-wrap">
|
||||
<cl-radio
|
||||
v-model="checked2"
|
||||
v-for="(item, index) in options"
|
||||
:key="index"
|
||||
:value="item.value"
|
||||
:pt="{
|
||||
className: 'mr-5'
|
||||
}"
|
||||
>
|
||||
{{ item.label }}
|
||||
</cl-radio>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('纵向排列')">
|
||||
<cl-radio
|
||||
v-model="checked"
|
||||
v-for="(item, index) in options"
|
||||
:key="index"
|
||||
:value="item.value"
|
||||
:pt="{
|
||||
className: parseClass([
|
||||
'my-2',
|
||||
[
|
||||
isVerticalCustom,
|
||||
'justify-between border border-solid border-surface-200 rounded-lg p-2 !my-1'
|
||||
]
|
||||
])
|
||||
}"
|
||||
>
|
||||
{{ item.label }}
|
||||
</cl-radio>
|
||||
|
||||
<cl-list
|
||||
border
|
||||
:pt="{
|
||||
className: 'mt-2'
|
||||
}"
|
||||
>
|
||||
<cl-list-item :label="t('换个样式')">
|
||||
<cl-switch v-model="isVerticalCustom"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义')">
|
||||
<view class="mb-3 flex flex-row flex-wrap">
|
||||
<cl-radio
|
||||
v-model="checked3"
|
||||
v-for="(item, index) in options"
|
||||
:key="index"
|
||||
:value="item.value"
|
||||
:disabled="isDisabled"
|
||||
:show-icon="!isHideIcon"
|
||||
:active-icon="isIcon ? 'heart-fill' : 'checkbox-circle-line'"
|
||||
:inactive-icon="isIcon ? 'heart-line' : 'checkbox-blank-circle-line'"
|
||||
:pt="{
|
||||
className: parseClass([
|
||||
'mr-5',
|
||||
[isCustom, 'bg-surface-100 py-2 px-3 rounded-lg !mr-2 !mb-2'],
|
||||
{
|
||||
'!bg-surface-700': isDark && isCustom
|
||||
}
|
||||
]),
|
||||
icon: {
|
||||
className: parseClass([
|
||||
[isCustom && item.value == checked3, 'text-red-500']
|
||||
])
|
||||
},
|
||||
label: {
|
||||
className: parseClass([
|
||||
[isCustom && item.value == checked3, 'text-red-500']
|
||||
])
|
||||
}
|
||||
}"
|
||||
>
|
||||
{{ item.label }}
|
||||
</cl-radio>
|
||||
</view>
|
||||
|
||||
<cl-list border>
|
||||
<cl-list-item :label="t('换个图标')">
|
||||
<cl-switch v-model="isIcon"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('不显示图标')">
|
||||
<cl-switch v-model="isHideIcon"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('禁用')">
|
||||
<cl-switch v-model="isDisabled"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('其他样式')">
|
||||
<cl-switch v-model="isCustom"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { parseClass } from "@/cool";
|
||||
import { useUi, type ClRadioOption } from "@/uni_modules/cool-ui";
|
||||
import { isDark } from "@/cool";
|
||||
import { t } from "@/locale";
|
||||
|
||||
const ui = useUi();
|
||||
|
||||
const isIcon = ref(false);
|
||||
const isCustom = ref(false);
|
||||
const isDisabled = ref(false);
|
||||
const isHideIcon = ref(false);
|
||||
const isVerticalCustom = ref(false);
|
||||
|
||||
const checked = ref("1");
|
||||
const checked2 = ref("2");
|
||||
const checked3 = ref("3");
|
||||
|
||||
const options = ref<ClRadioOption[]>([
|
||||
{
|
||||
label: "Vue",
|
||||
value: "1"
|
||||
},
|
||||
{
|
||||
label: "React",
|
||||
value: "2"
|
||||
},
|
||||
{
|
||||
label: "Angular",
|
||||
value: "3"
|
||||
},
|
||||
{
|
||||
label: "Svelte",
|
||||
value: "4"
|
||||
}
|
||||
]);
|
||||
</script>
|
||||
62
cool-unix/pages/demo/form/rate.uvue
Normal file
62
cool-unix/pages/demo/form/rate.uvue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-rate v-model="num"></cl-rate>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义')">
|
||||
<cl-rate
|
||||
v-model="num2"
|
||||
:disabled="isDisabled"
|
||||
:show-score="isShowScore"
|
||||
:allow-half="isAllowHalf"
|
||||
:size="isSize ? 50 : 40"
|
||||
:void-icon="isIcon ? 'heart-fill' : 'star-fill'"
|
||||
:icon="isIcon ? 'heart-fill' : 'star-fill'"
|
||||
></cl-rate>
|
||||
|
||||
<cl-list
|
||||
border
|
||||
:pt="{
|
||||
className: 'mt-3'
|
||||
}"
|
||||
>
|
||||
<cl-list-item :label="t('只读')">
|
||||
<cl-switch v-model="isDisabled"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('显示分数')">
|
||||
<cl-switch v-model="isShowScore"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('允许半星')">
|
||||
<cl-switch v-model="isAllowHalf"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('换个图标')">
|
||||
<cl-switch v-model="isIcon"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('大一点')">
|
||||
<cl-switch v-model="isSize"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { t } from "@/locale";
|
||||
|
||||
const num = ref(1);
|
||||
const num2 = ref(3.5);
|
||||
const isDisabled = ref(false);
|
||||
const isShowScore = ref(true);
|
||||
const isAllowHalf = ref(true);
|
||||
const isSize = ref(false);
|
||||
const isIcon = ref(false);
|
||||
</script>
|
||||
286
cool-unix/pages/demo/form/select-date.uvue
Normal file
286
cool-unix/pages/demo/form/select-date.uvue
Normal file
@@ -0,0 +1,286 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-select-date
|
||||
v-model="form.date1"
|
||||
type="date"
|
||||
@change="onDateChange"
|
||||
></cl-select-date>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('固定开始、结束日期')">
|
||||
<cl-select-date
|
||||
v-model="form.date3"
|
||||
start="2025-06-01"
|
||||
end="2027-08-01"
|
||||
type="date"
|
||||
></cl-select-date>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义触发器')">
|
||||
<view class="flex flex-row">
|
||||
<cl-button @tap="openSelect4">{{ t("打开选择器") }}</cl-button>
|
||||
</view>
|
||||
|
||||
<cl-select-date
|
||||
ref="selectRef4"
|
||||
v-model="form.date4"
|
||||
type="date"
|
||||
:show-trigger="false"
|
||||
></cl-select-date>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('范围选择')">
|
||||
<cl-text :pt="{ className: 'mb-3' }">{{ form.date5 }}</cl-text>
|
||||
<cl-select-date v-model:values="form.date5" type="date" rangeable></cl-select-date>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义快捷选项')">
|
||||
<cl-select-date
|
||||
v-model:values="form.date6"
|
||||
type="date"
|
||||
rangeable
|
||||
:shortcuts="shortcuts"
|
||||
></cl-select-date>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义')">
|
||||
<cl-select-date
|
||||
v-model="form.date7"
|
||||
:type="type"
|
||||
:label-format="labelFormat"
|
||||
:disabled="isDisabled"
|
||||
></cl-select-date>
|
||||
|
||||
<cl-list
|
||||
border
|
||||
:pt="{
|
||||
className: 'mt-3'
|
||||
}"
|
||||
>
|
||||
<cl-list-item label="YYYY">
|
||||
<cl-switch v-model="isYear"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item label="YYYY-MM">
|
||||
<cl-switch v-model="isMonth"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item
|
||||
label="YYYY-MM-DD"
|
||||
:pt="{
|
||||
label: {
|
||||
className: 'flex-[2]'
|
||||
}
|
||||
}"
|
||||
>
|
||||
<cl-switch v-model="isDate"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item
|
||||
label="YYYY-MM-DD HH"
|
||||
:pt="{
|
||||
label: {
|
||||
className: 'flex-[2]'
|
||||
}
|
||||
}"
|
||||
>
|
||||
<cl-switch v-model="isHour"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item
|
||||
label="YYYY-MM-DD HH:mm"
|
||||
:pt="{
|
||||
label: {
|
||||
className: 'flex-[2]'
|
||||
}
|
||||
}"
|
||||
>
|
||||
<cl-switch v-model="isMinute"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item
|
||||
label="YYYY-MM-DD HH:mm:ss"
|
||||
:pt="{
|
||||
label: {
|
||||
className: 'flex-[2]'
|
||||
}
|
||||
}"
|
||||
>
|
||||
<cl-switch v-model="isSecond"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('标签格式化')">
|
||||
<cl-switch v-model="isFormat"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('禁用')">
|
||||
<cl-switch v-model="isDisabled"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, reactive, ref } from "vue";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { t } from "@/locale";
|
||||
import { useUi, type ClSelectDateShortcut } from "@/uni_modules/cool-ui";
|
||||
import { dayUts } from "@/cool";
|
||||
|
||||
const ui = useUi();
|
||||
|
||||
// 测试日期选择变化事件
|
||||
function onDateChange(value: string) {
|
||||
console.log("日期选择变化:", value);
|
||||
ui.showToast({
|
||||
message: `选择了日期: ${value}`,
|
||||
type: "success"
|
||||
});
|
||||
}
|
||||
|
||||
type Form = {
|
||||
date1: string;
|
||||
date2: string;
|
||||
date3: string;
|
||||
date4: string;
|
||||
date5: string[];
|
||||
date6: string[];
|
||||
date7: string;
|
||||
};
|
||||
|
||||
const form = reactive<Form>({
|
||||
date1: "2023-06-24",
|
||||
date2: "",
|
||||
date3: "",
|
||||
date4: "",
|
||||
date5: [],
|
||||
date6: [],
|
||||
date7: ""
|
||||
});
|
||||
|
||||
const isDisabled = ref(false);
|
||||
const isYear = ref(false);
|
||||
const isMonth = ref(false);
|
||||
const isDate = ref(true);
|
||||
const isHour = ref(false);
|
||||
const isMinute = ref(false);
|
||||
const isSecond = ref(false);
|
||||
const isFormat = ref(false);
|
||||
|
||||
const type = computed(() => {
|
||||
if (isSecond.value) {
|
||||
return "second";
|
||||
}
|
||||
|
||||
if (isMinute.value) {
|
||||
return "minute";
|
||||
}
|
||||
|
||||
if (isHour.value) {
|
||||
return "hour";
|
||||
}
|
||||
|
||||
if (isDate.value) {
|
||||
return "date";
|
||||
}
|
||||
|
||||
if (isMonth.value) {
|
||||
return "month";
|
||||
}
|
||||
|
||||
if (isYear.value) {
|
||||
return "year";
|
||||
}
|
||||
|
||||
return "second";
|
||||
});
|
||||
|
||||
const labelFormat = computed(() => {
|
||||
if (isFormat.value) {
|
||||
if (isSecond.value) {
|
||||
return "YYYY年MM月DD日 HH时mm分ss秒";
|
||||
}
|
||||
|
||||
if (isMinute.value) {
|
||||
return "YYYY年MM月DD日 HH时mm分";
|
||||
}
|
||||
|
||||
if (isHour.value) {
|
||||
return "YYYY年MM月DD日 HH时";
|
||||
}
|
||||
|
||||
if (isDate.value) {
|
||||
return "YYYY年MM月DD日";
|
||||
}
|
||||
|
||||
if (isMonth.value) {
|
||||
return "YYYY年MM月";
|
||||
}
|
||||
|
||||
if (isYear.value) {
|
||||
return "YYYY年";
|
||||
}
|
||||
} else {
|
||||
if (isSecond.value) {
|
||||
return "YYYY-MM-DD HH:mm:ss";
|
||||
}
|
||||
|
||||
if (isMinute.value) {
|
||||
return "YYYY-MM-DD HH:mm";
|
||||
}
|
||||
|
||||
if (isHour.value) {
|
||||
return "YYYY-MM-DD HH";
|
||||
}
|
||||
|
||||
if (isDate.value) {
|
||||
return "YYYY-MM-DD";
|
||||
}
|
||||
|
||||
if (isMonth.value) {
|
||||
return "YYYY-MM";
|
||||
}
|
||||
|
||||
if (isYear.value) {
|
||||
return "YYYY";
|
||||
}
|
||||
}
|
||||
|
||||
return "YYYY-MM-DD HH:mm:ss";
|
||||
});
|
||||
|
||||
const selectRef4 = ref<ClSelectDateComponentPublicInstance | null>(null);
|
||||
|
||||
function openSelect4() {
|
||||
selectRef4.value!.open((value) => {
|
||||
ui.showToast({
|
||||
message: `你选择了:${value}`
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const shortcuts = ref<ClSelectDateShortcut[]>([
|
||||
{
|
||||
label: "昨日",
|
||||
value: [dayUts().subtract(1, "day").format("YYYY-MM-DD"), dayUts().format("YYYY-MM-DD")]
|
||||
},
|
||||
{
|
||||
label: "本周",
|
||||
value: [
|
||||
dayUts().startOf("week").add(1, "day").format("YYYY-MM-DD"),
|
||||
dayUts().endOf("week").add(1, "day").format("YYYY-MM-DD")
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "本月",
|
||||
value: [
|
||||
dayUts().startOf("month").format("YYYY-MM-DD"),
|
||||
dayUts().endOf("month").format("YYYY-MM-DD")
|
||||
]
|
||||
}
|
||||
]);
|
||||
</script>
|
||||
123
cool-unix/pages/demo/form/select-time.uvue
Normal file
123
cool-unix/pages/demo/form/select-time.uvue
Normal file
@@ -0,0 +1,123 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-select-time v-model="form.time1"></cl-select-time>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义触发器')">
|
||||
<view class="flex flex-row">
|
||||
<cl-button @tap="openSelect2">{{ t("打开选择器") }} </cl-button>
|
||||
</view>
|
||||
|
||||
<cl-select-time
|
||||
ref="selectRef2"
|
||||
v-model="form.time2"
|
||||
:show-trigger="false"
|
||||
></cl-select-time>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义')">
|
||||
<cl-select-time
|
||||
v-model="form.time3"
|
||||
:disabled="isDisabled"
|
||||
:label-format="labelFormat"
|
||||
:type="type"
|
||||
></cl-select-time>
|
||||
|
||||
<cl-list
|
||||
border
|
||||
:pt="{
|
||||
className: 'mt-3'
|
||||
}"
|
||||
>
|
||||
<cl-list-item :label="t('时')">
|
||||
<cl-switch v-model="isHour"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('时:分')">
|
||||
<cl-switch v-model="isMinute"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('时:分:秒')">
|
||||
<cl-switch v-model="isSecond"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('标签格式化')">
|
||||
<cl-switch v-model="isFormat"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('禁用')">
|
||||
<cl-switch v-model="isDisabled"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, reactive, ref } from "vue";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
import { t } from "@/locale";
|
||||
|
||||
const ui = useUi();
|
||||
|
||||
type Form = {
|
||||
time1: string;
|
||||
time2: string;
|
||||
time3: string;
|
||||
};
|
||||
|
||||
const form = reactive<Form>({
|
||||
time1: "",
|
||||
time2: "",
|
||||
time3: ""
|
||||
});
|
||||
|
||||
const isDisabled = ref(false);
|
||||
const isFormat = ref(false);
|
||||
const isHour = ref(false);
|
||||
const isMinute = ref(false);
|
||||
const isSecond = ref(true);
|
||||
|
||||
const selectRef2 = ref<ClSelectTimeComponentPublicInstance | null>(null);
|
||||
|
||||
function openSelect2() {
|
||||
selectRef2.value!.open((value) => {
|
||||
ui.showToast({
|
||||
message: `你选择了:${value}`
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const type = computed(() => {
|
||||
if (isHour.value) {
|
||||
return "hour";
|
||||
}
|
||||
|
||||
if (isMinute.value) {
|
||||
return "minute";
|
||||
}
|
||||
|
||||
return "second";
|
||||
});
|
||||
|
||||
const labelFormat = computed(() => {
|
||||
if (isFormat.value) {
|
||||
switch (type.value) {
|
||||
case "hour":
|
||||
return "{H}时";
|
||||
case "minute":
|
||||
return "{H}时{m}分";
|
||||
case "second":
|
||||
return "{H}时{m}分{s}秒";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
388
cool-unix/pages/demo/form/select.uvue
Normal file
388
cool-unix/pages/demo/form/select.uvue
Normal file
@@ -0,0 +1,388 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-select v-model="form.selected" :options="options"></cl-select>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义触发器')">
|
||||
<view class="flex flex-row">
|
||||
<cl-button @tap="openSelect2">{{ t("打开选择器") }}</cl-button>
|
||||
</view>
|
||||
|
||||
<cl-select
|
||||
ref="selectRef2"
|
||||
v-model="form.selected2"
|
||||
:options="options2"
|
||||
:show-trigger="false"
|
||||
></cl-select>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('多列')">
|
||||
<demo-tips>
|
||||
{{ t("通过 children 配置多级数据,并使用 column-count 参数指定显示的列数") }}
|
||||
</demo-tips>
|
||||
|
||||
<cl-select
|
||||
v-model="form.selected3"
|
||||
:options="options3"
|
||||
:column-count="3"
|
||||
></cl-select>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('弹窗中使用')">
|
||||
<cl-button @tap="visible3 = true">{{ t("打开") }}</cl-button>
|
||||
|
||||
<cl-popup v-model="visible3" direction="center" size="80%" :title="t('选择地区')">
|
||||
<view class="p-3 pt-0">
|
||||
<demo-tips>
|
||||
H5 和 APP 端通过 teleport 实现弹窗内的选择器使用,小程序端则通过
|
||||
root-portal 实现。
|
||||
</demo-tips>
|
||||
|
||||
<cl-select
|
||||
v-model="form.selected3"
|
||||
:options="options3"
|
||||
:column-count="3"
|
||||
></cl-select>
|
||||
</view>
|
||||
</cl-popup>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义')">
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'mb-3 text-sm p-3 bg-surface-100 dark:!bg-surface-700 rounded-lg'
|
||||
}"
|
||||
v-if="form.selected4 != null && isShowValue"
|
||||
>
|
||||
{{ t("绑定值") }}:{{ form.selected4 }}
|
||||
</cl-text>
|
||||
|
||||
<cl-select
|
||||
v-model="form.selected4"
|
||||
:options="options"
|
||||
:disabled="isDisabled"
|
||||
:show-cancel="isShowCancel"
|
||||
:confirm-text="isButtonText ? t('下一步') : t('确定')"
|
||||
:cancel-text="isButtonText ? t('关闭') : t('取消')"
|
||||
></cl-select>
|
||||
|
||||
<cl-list
|
||||
border
|
||||
:pt="{
|
||||
className: 'mt-3'
|
||||
}"
|
||||
>
|
||||
<cl-list-item :label="t('显示取消按钮')">
|
||||
<cl-switch v-model="isShowCancel"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('修改按钮文案')">
|
||||
<cl-switch v-model="isButtonText"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('禁用')">
|
||||
<cl-switch v-model="isDisabled"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('显示绑定值')">
|
||||
<cl-switch v-model="isShowValue"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('空数据')">
|
||||
<cl-select v-model="form.selected" :options="options4"></cl-select>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from "vue";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import DemoTips from "../components/tips.uvue";
|
||||
import { useUi, type ClSelectOption } from "@/uni_modules/cool-ui";
|
||||
import { t } from "@/locale";
|
||||
|
||||
const ui = useUi();
|
||||
|
||||
const selectRef2 = ref<ClSelectComponentPublicInstance | null>(null);
|
||||
|
||||
const isShowCancel = ref(true);
|
||||
const isButtonText = ref(false);
|
||||
const isDisabled = ref(false);
|
||||
const isShowValue = ref(false);
|
||||
|
||||
type Form = {
|
||||
selected: number | null; // 参数 clear 需要指定类型带有 null
|
||||
selected2: string;
|
||||
selected3: number[];
|
||||
selected4: number | null;
|
||||
};
|
||||
|
||||
const form = reactive<Form>({
|
||||
selected: null,
|
||||
selected2: "2",
|
||||
selected3: [],
|
||||
selected4: 3
|
||||
});
|
||||
|
||||
const options = ref<ClSelectOption[]>([
|
||||
{
|
||||
label: "HTML",
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: "CSS",
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
label: "JavaScript",
|
||||
value: 3
|
||||
},
|
||||
{
|
||||
label: "Node",
|
||||
value: 4
|
||||
},
|
||||
{
|
||||
label: "Vite",
|
||||
value: 5
|
||||
},
|
||||
{
|
||||
label: "Webpack",
|
||||
value: 6
|
||||
}
|
||||
]);
|
||||
|
||||
const options2 = ref<ClSelectOption[]>([
|
||||
{
|
||||
label: "Vue",
|
||||
value: "1"
|
||||
},
|
||||
{
|
||||
label: "React",
|
||||
value: "2"
|
||||
},
|
||||
{
|
||||
label: "Angular",
|
||||
value: "3"
|
||||
},
|
||||
{
|
||||
label: "Svelte",
|
||||
value: "4"
|
||||
}
|
||||
]);
|
||||
|
||||
const options3 = ref<ClSelectOption[]>([
|
||||
{
|
||||
label: "福建省",
|
||||
value: 1,
|
||||
children: [
|
||||
{
|
||||
label: "福州市",
|
||||
value: 1,
|
||||
children: [
|
||||
{
|
||||
label: "鼓楼区",
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: "台江区",
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
label: "仓山区",
|
||||
value: 3
|
||||
},
|
||||
{
|
||||
label: "马尾区",
|
||||
value: 4
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "厦门市",
|
||||
value: 2,
|
||||
children: [
|
||||
{
|
||||
label: "思明区",
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: "湖里区",
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
label: "集美区",
|
||||
value: 3
|
||||
},
|
||||
{
|
||||
label: "海沧区",
|
||||
value: 4
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "泉州市",
|
||||
value: 3,
|
||||
children: [
|
||||
{
|
||||
label: "鲤城区",
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: "丰泽区",
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
label: "洛江区",
|
||||
value: 3
|
||||
},
|
||||
{
|
||||
label: "泉港区",
|
||||
value: 4
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "浙江省",
|
||||
value: 2,
|
||||
children: [
|
||||
{
|
||||
label: "杭州市",
|
||||
value: 1,
|
||||
children: [
|
||||
{
|
||||
label: "上城区",
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: "下城区",
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
label: "江干区",
|
||||
value: 3
|
||||
},
|
||||
{
|
||||
label: "拱墅区",
|
||||
value: 4
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "宁波市",
|
||||
value: 2,
|
||||
children: [
|
||||
{
|
||||
label: "海曙区",
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: "江北区",
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
label: "北仑区",
|
||||
value: 3
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "湖南省",
|
||||
value: 3,
|
||||
children: [
|
||||
{
|
||||
label: "长沙市",
|
||||
value: 1,
|
||||
children: [
|
||||
{
|
||||
label: "芙蓉区",
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: "天心区",
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
label: "岳麓区",
|
||||
value: 3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "株洲市",
|
||||
value: 2,
|
||||
children: [
|
||||
{
|
||||
label: "荷塘区",
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: "芦淞区",
|
||||
value: 2
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "江西省",
|
||||
value: 4,
|
||||
children: [
|
||||
{
|
||||
label: "南昌市",
|
||||
value: 1,
|
||||
children: [
|
||||
{
|
||||
label: "东湖区",
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: "西湖区",
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
label: "青云谱区",
|
||||
value: 3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "九江市",
|
||||
value: 2,
|
||||
children: [
|
||||
{
|
||||
label: "浔阳区",
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: "濂溪区",
|
||||
value: 2
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
const options4 = ref<ClSelectOption[]>([]);
|
||||
|
||||
function openSelect2() {
|
||||
selectRef2.value!.open((value) => {
|
||||
const d = options2.value.find((item) => item.value == value);
|
||||
|
||||
ui.showToast({
|
||||
message: `你选择了 ${value} : ${d?.label}`
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const visible3 = ref(false);
|
||||
</script>
|
||||
86
cool-unix/pages/demo/form/slider.uvue
Normal file
86
cool-unix/pages/demo/form/slider.uvue
Normal file
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-slider v-model="num"></cl-slider>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('范围选择')">
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'mb-3'
|
||||
}"
|
||||
>{{ num2[0] }} ~ {{ num2[1] }}</cl-text
|
||||
>
|
||||
<cl-slider v-model:values="num2" range></cl-slider>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义')">
|
||||
<cl-slider
|
||||
v-model="num3"
|
||||
:disabled="isDisabled"
|
||||
:show-value="isShowValue"
|
||||
:block-size="isSize ? 50 : 40"
|
||||
:track-height="isSize ? 12 : 8"
|
||||
:step="isStep ? 10 : 1"
|
||||
:max="isMax ? 50 : 100"
|
||||
:pt="{
|
||||
thumb: {
|
||||
className: isColor ? '!bg-red-500' : ''
|
||||
},
|
||||
progress: {
|
||||
className: isColor ? '!bg-red-200' : ''
|
||||
}
|
||||
}"
|
||||
></cl-slider>
|
||||
|
||||
<cl-list
|
||||
border
|
||||
:pt="{
|
||||
className: 'mt-3'
|
||||
}"
|
||||
>
|
||||
<cl-list-item :label="t('显示值')">
|
||||
<cl-switch v-model="isShowValue"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('步长10')">
|
||||
<cl-switch v-model="isStep"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('滑块大点')">
|
||||
<cl-switch v-model="isSize"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('换个颜色')">
|
||||
<cl-switch v-model="isColor"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('最大50')">
|
||||
<cl-switch v-model="isMax"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('禁用')">
|
||||
<cl-switch v-model="isDisabled"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { t } from "@/locale";
|
||||
|
||||
const num = ref(60);
|
||||
const num2 = ref<number[]>([10, 20]);
|
||||
const num3 = ref(35);
|
||||
const isDisabled = ref(false);
|
||||
const isShowValue = ref(true);
|
||||
const isStep = ref(false);
|
||||
const isSize = ref(false);
|
||||
const isMax = ref(false);
|
||||
const isColor = ref(false);
|
||||
</script>
|
||||
92
cool-unix/pages/demo/form/switch.uvue
Normal file
92
cool-unix/pages/demo/form/switch.uvue
Normal file
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-switch v-model="checked"></cl-switch>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义颜色')">
|
||||
<view class="flex flex-row">
|
||||
<cl-switch
|
||||
v-model="checked"
|
||||
:pt="{
|
||||
className: 'mr-5',
|
||||
track: {
|
||||
className: '!bg-red-100'
|
||||
},
|
||||
thumb: {
|
||||
className: '!bg-red-500'
|
||||
}
|
||||
}"
|
||||
></cl-switch>
|
||||
|
||||
<cl-switch
|
||||
v-model="checked"
|
||||
:pt="{
|
||||
track: {
|
||||
className: '!bg-purple-100'
|
||||
},
|
||||
thumb: {
|
||||
className: '!bg-purple-500'
|
||||
}
|
||||
}"
|
||||
></cl-switch>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义')">
|
||||
<cl-switch
|
||||
v-model="checked"
|
||||
:loading="isLoading"
|
||||
:disabled="isDisabled"
|
||||
:height="isSize ? 60 : 48"
|
||||
:width="isSize ? 100 : 80"
|
||||
:pt="{
|
||||
track: {
|
||||
className: parseClass([[isCustom, '!rounded-md']])
|
||||
},
|
||||
thumb: {
|
||||
className: parseClass([[isCustom, '!rounded-md']])
|
||||
}
|
||||
}"
|
||||
></cl-switch>
|
||||
|
||||
<cl-list
|
||||
border
|
||||
:pt="{
|
||||
className: 'mt-3'
|
||||
}"
|
||||
>
|
||||
<cl-list-item :label="t('禁用')">
|
||||
<cl-switch v-model="isDisabled"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('加载中')">
|
||||
<cl-switch v-model="isLoading"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('大一点')">
|
||||
<cl-switch v-model="isSize"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('正方形')">
|
||||
<cl-switch v-model="isCustom"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { parseClass } from "@/cool";
|
||||
import { t } from "@/locale";
|
||||
|
||||
const checked = ref(false);
|
||||
const isDisabled = ref(false);
|
||||
const isLoading = ref(false);
|
||||
const isSize = ref(false);
|
||||
const isCustom = ref(false);
|
||||
</script>
|
||||
67
cool-unix/pages/demo/form/textarea.uvue
Normal file
67
cool-unix/pages/demo/form/textarea.uvue
Normal file
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-textarea></cl-textarea>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义')">
|
||||
<cl-textarea
|
||||
v-model="content"
|
||||
:border="isBorder"
|
||||
:disabled="isDisabled"
|
||||
:show-word-limit="isShowCount"
|
||||
:auto-height="isAutoHeight"
|
||||
:pt="{
|
||||
className: parseClass({
|
||||
'!bg-sky-100': isColor,
|
||||
'!border-sky-700': isColor
|
||||
}),
|
||||
inner: {
|
||||
className: parseClass({
|
||||
'text-sky-700': isColor
|
||||
})
|
||||
}
|
||||
}"
|
||||
></cl-textarea>
|
||||
|
||||
<cl-list border :pt="{ className: 'mt-5' }">
|
||||
<cl-list-item :label="t('边框')">
|
||||
<cl-switch v-model="isBorder"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('显示字数')">
|
||||
<cl-switch v-model="isShowCount"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('自动增高')">
|
||||
<cl-switch v-model="isAutoHeight"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('禁用')">
|
||||
<cl-switch v-model="isDisabled"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('其他颜色')">
|
||||
<cl-switch v-model="isColor"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { parseClass } from "@/cool";
|
||||
import { t } from "@/locale";
|
||||
|
||||
const content = ref("Cool Unix");
|
||||
|
||||
const isBorder = ref(true);
|
||||
const isShowCount = ref(true);
|
||||
const isDisabled = ref(false);
|
||||
const isColor = ref(false);
|
||||
const isAutoHeight = ref(false);
|
||||
</script>
|
||||
50
cool-unix/pages/demo/form/upload.uvue
Normal file
50
cool-unix/pages/demo/form/upload.uvue
Normal file
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-upload v-model="form.upload1" test></cl-upload>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('禁用')">
|
||||
<cl-upload v-model="form.upload1" disabled test></cl-upload>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义图标、文字、大小')">
|
||||
<cl-upload
|
||||
v-model="form.upload1"
|
||||
icon="id-card-line"
|
||||
:text="t('上传证件照')"
|
||||
:width="300"
|
||||
:height="200"
|
||||
test
|
||||
></cl-upload>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('多选')">
|
||||
<cl-upload multiple v-model="form.upload2" test></cl-upload>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('限制 3 个')">
|
||||
<cl-upload multiple :limit="3" v-model="form.upload3" test></cl-upload>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { reactive } from "vue";
|
||||
|
||||
type Form = {
|
||||
upload1: string;
|
||||
upload2: string[];
|
||||
upload3: string[];
|
||||
};
|
||||
|
||||
const form = reactive<Form>({
|
||||
upload1: "",
|
||||
upload2: [],
|
||||
upload3: []
|
||||
});
|
||||
</script>
|
||||
45
cool-unix/pages/demo/layout/collapse.uvue
Normal file
45
cool-unix/pages/demo/layout/collapse.uvue
Normal file
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-button @click="toggle">{{ visible ? t("点击收起") : t("点击展开") }}</cl-button>
|
||||
|
||||
<cl-collapse v-model="visible">
|
||||
<cl-text
|
||||
>云想衣裳花想容,春风拂槛露华浓,若非群玉山头见,会向瑶台月下逢。</cl-text
|
||||
>
|
||||
</cl-collapse>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('ref 方式调用')">
|
||||
<cl-button @click="refToggle">{{ t("点击展开") }}</cl-button>
|
||||
|
||||
<cl-collapse ref="collapseRef">
|
||||
<view class="bg-surface-100 p-3 rounded-xl dark:!bg-surface-700">
|
||||
<cl-text
|
||||
>云想衣裳花想容,春风拂槛露华浓,若非群玉山头见,会向瑶台月下逢。</cl-text
|
||||
>
|
||||
</view>
|
||||
</cl-collapse>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { ref } from "vue";
|
||||
|
||||
const visible = ref(false);
|
||||
|
||||
function toggle() {
|
||||
visible.value = !visible.value;
|
||||
}
|
||||
|
||||
const collapseRef = ref<ClCollapseComponentPublicInstance | null>(null);
|
||||
|
||||
function refToggle() {
|
||||
collapseRef.value!.toggle();
|
||||
}
|
||||
</script>
|
||||
183
cool-unix/pages/demo/layout/flex.uvue
Normal file
183
cool-unix/pages/demo/layout/flex.uvue
Normal file
@@ -0,0 +1,183 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view
|
||||
class="p-3"
|
||||
:class="{
|
||||
'is-dark': isDark
|
||||
}"
|
||||
>
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-row :gutter="12">
|
||||
<cl-col :span="8">
|
||||
<view class="item">
|
||||
<cl-text :pt="{ className: 'text' }">1</cl-text>
|
||||
</view>
|
||||
</cl-col>
|
||||
<cl-col :span="8">
|
||||
<view class="item">
|
||||
<cl-text :pt="{ className: 'text' }">2</cl-text>
|
||||
</view>
|
||||
</cl-col>
|
||||
<cl-col :span="8">
|
||||
<view class="item">
|
||||
<cl-text :pt="{ className: 'text' }">3</cl-text>
|
||||
</view>
|
||||
</cl-col>
|
||||
</cl-row>
|
||||
|
||||
<cl-row :gutter="12" :pt="{ className: 'mt-3' }">
|
||||
<cl-col :span="12">
|
||||
<view class="item">
|
||||
<cl-text :pt="{ className: 'text' }">1</cl-text>
|
||||
</view>
|
||||
</cl-col>
|
||||
<cl-col :span="12">
|
||||
<view class="item">
|
||||
<cl-text :pt="{ className: 'text' }">2</cl-text>
|
||||
</view>
|
||||
</cl-col>
|
||||
</cl-row>
|
||||
|
||||
<cl-row :gutter="12" :pt="{ className: 'mt-3' }">
|
||||
<cl-col :span="6">
|
||||
<view class="item">
|
||||
<cl-text :pt="{ className: 'text' }">1</cl-text>
|
||||
</view>
|
||||
</cl-col>
|
||||
<cl-col :span="6">
|
||||
<view class="item">
|
||||
<cl-text :pt="{ className: 'text' }">2</cl-text>
|
||||
</view>
|
||||
</cl-col>
|
||||
<cl-col :span="6">
|
||||
<view class="item">
|
||||
<cl-text :pt="{ className: 'text' }">3</cl-text>
|
||||
</view>
|
||||
</cl-col>
|
||||
<cl-col :span="6">
|
||||
<view class="item">
|
||||
<cl-text :pt="{ className: 'text' }">4</cl-text>
|
||||
</view>
|
||||
</cl-col>
|
||||
</cl-row>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('左间隔')">
|
||||
<cl-row :gutter="12">
|
||||
<cl-col :span="6">
|
||||
<view class="item">
|
||||
<cl-text :pt="{ className: 'text' }">1</cl-text>
|
||||
</view>
|
||||
</cl-col>
|
||||
<cl-col :span="6" :offset="6">
|
||||
<view class="item active">
|
||||
<cl-text :pt="{ className: 'text' }">2</cl-text>
|
||||
</view>
|
||||
</cl-col>
|
||||
<cl-col :span="6">
|
||||
<view class="item">
|
||||
<cl-text :pt="{ className: 'text' }">3</cl-text>
|
||||
</view>
|
||||
</cl-col>
|
||||
</cl-row>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('右移动')">
|
||||
<cl-row :gutter="12">
|
||||
<cl-col :span="6">
|
||||
<view class="item">
|
||||
<cl-text :pt="{ className: 'text' }">1</cl-text>
|
||||
</view>
|
||||
</cl-col>
|
||||
<cl-col :span="6">
|
||||
<view class="item">
|
||||
<cl-text :pt="{ className: 'text' }">2</cl-text>
|
||||
</view>
|
||||
</cl-col>
|
||||
<cl-col :span="6" :push="6">
|
||||
<view class="item active">
|
||||
<cl-text :pt="{ className: 'text' }">3</cl-text>
|
||||
</view>
|
||||
</cl-col>
|
||||
</cl-row>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('左移动')">
|
||||
<cl-row :gutter="12">
|
||||
<cl-col :span="6">
|
||||
<view class="item">
|
||||
<cl-text :pt="{ className: 'text' }">1</cl-text>
|
||||
</view>
|
||||
</cl-col>
|
||||
<cl-col :span="6">
|
||||
<view class="item">
|
||||
<cl-text :pt="{ className: 'text' }">2</cl-text>
|
||||
</view>
|
||||
</cl-col>
|
||||
<cl-col :span="6">
|
||||
<view class="item">
|
||||
<cl-text :pt="{ className: 'text' }">3</cl-text>
|
||||
</view>
|
||||
</cl-col>
|
||||
<cl-col :span="6" :pull="6">
|
||||
<view class="item active">
|
||||
<cl-text :pt="{ className: 'text' }">4</cl-text>
|
||||
</view>
|
||||
</cl-col>
|
||||
</cl-row>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('多个数据')">
|
||||
<cl-row :gutter="12">
|
||||
<cl-col :span="4" v-for="item in 20" :key="item">
|
||||
<view class="item mb-2">
|
||||
<cl-text :pt="{ className: 'text' }">{{ item }}</cl-text>
|
||||
</view>
|
||||
</cl-col>
|
||||
</cl-row>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { isDark } from "@/cool";
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.item {
|
||||
@apply h-8 bg-surface-100 rounded-md flex flex-row items-center justify-center;
|
||||
|
||||
.text {
|
||||
@apply text-sm text-surface-700;
|
||||
}
|
||||
|
||||
&.active {
|
||||
@apply bg-primary-500;
|
||||
|
||||
.text {
|
||||
@apply text-white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.is-dark {
|
||||
.item {
|
||||
@apply bg-surface-700;
|
||||
|
||||
.text {
|
||||
@apply text-surface-100;
|
||||
}
|
||||
|
||||
&.active {
|
||||
@apply bg-primary-500;
|
||||
|
||||
.text {
|
||||
@apply text-white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
50
cool-unix/pages/demo/layout/float-view.uvue
Normal file
50
cool-unix/pages/demo/layout/float-view.uvue
Normal file
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item>
|
||||
<view class="flex flex-row items-center">
|
||||
<cl-icon name="notification-4-fill" class="mr-2"></cl-icon>
|
||||
<cl-text>{{ t("禁用状态,无法拖拽") }}</cl-text>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item>
|
||||
<view class="flex flex-row items-center">
|
||||
<cl-icon name="heart-fill" class="mr-2"></cl-icon>
|
||||
<cl-text>{{ t("不吸附边缘,任意位置可拖拽") }}</cl-text>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<cl-float-view :left="200" :bottom="50" :no-snapping="true">
|
||||
<view
|
||||
class="w-[40px] h-[40px] bg-primary-500 flex flex-row items-center justify-center"
|
||||
@tap="toAlert"
|
||||
>
|
||||
<cl-icon name="heart-fill" color="white"></cl-icon>
|
||||
</view>
|
||||
</cl-float-view>
|
||||
|
||||
<cl-float-view disabled :left="20" :bottom="400" :gap="10">
|
||||
<view
|
||||
class="w-[40px] h-[40px] bg-surface-400 rounded-full flex flex-row items-center justify-center"
|
||||
>
|
||||
<cl-icon name="notification-4-fill" color="white"></cl-icon>
|
||||
</view>
|
||||
</cl-float-view>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
|
||||
const ui = useUi();
|
||||
|
||||
function toAlert() {
|
||||
ui.showToast({
|
||||
message: t("这是一个提示")
|
||||
});
|
||||
}
|
||||
</script>
|
||||
63
cool-unix/pages/demo/layout/footer.uvue
Normal file
63
cool-unix/pages/demo/layout/footer.uvue
Normal file
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item>
|
||||
<cl-text>解决底部按钮隐藏时页面底部仍有空白间距</cl-text>
|
||||
<cl-text>解决固定定位时内容占位缺失</cl-text>
|
||||
</demo-item>
|
||||
|
||||
<cl-list>
|
||||
<cl-list-item :label="`${i}`" v-for="i in 50" :key="i"> </cl-list-item>
|
||||
</cl-list>
|
||||
</view>
|
||||
|
||||
<cl-footer :vt="cache.key">
|
||||
<template v-if="status == 0">
|
||||
<view class="flex flex-row">
|
||||
<cl-button :pt="{ className: 'flex-1' }" text border size="large" @tap="cancel">
|
||||
{{ t("取消订单") }}
|
||||
</cl-button>
|
||||
|
||||
<cl-button :pt="{ className: 'flex-1' }" type="primary" size="large" @tap="buy">
|
||||
{{ t("立即购买") }}
|
||||
</cl-button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<cl-button type="error" size="large" @tap="confirm" v-if="status == 1">
|
||||
{{ t("确认收货") }}
|
||||
</cl-button>
|
||||
|
||||
<cl-button type="success" size="large" @tap="comment" v-if="status == 2">
|
||||
{{ t("评价") }}
|
||||
</cl-button>
|
||||
</cl-footer>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useCache } from "@/cool";
|
||||
import { t } from "@/locale";
|
||||
import { ref } from "vue";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
|
||||
const status = ref(0);
|
||||
|
||||
const { cache } = useCache(() => [status.value]);
|
||||
|
||||
function cancel() {
|
||||
status.value = 3;
|
||||
}
|
||||
|
||||
function buy() {
|
||||
status.value = 1;
|
||||
}
|
||||
|
||||
function confirm() {
|
||||
status.value = 2;
|
||||
}
|
||||
|
||||
function comment() {
|
||||
status.value = 3;
|
||||
}
|
||||
</script>
|
||||
44
cool-unix/pages/demo/layout/sticky.uvue
Normal file
44
cool-unix/pages/demo/layout/sticky.uvue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<cl-page back-top>
|
||||
<cl-sticky>
|
||||
<view class="bg-primary-500 p-3 h-[40px] flex flex-row items-center">
|
||||
<cl-text color="white">Header - 1</cl-text>
|
||||
</view>
|
||||
</cl-sticky>
|
||||
|
||||
<view class="p-3">
|
||||
<cl-list>
|
||||
<cl-list-item :label="`${i}`" v-for="i in 50" :key="i"> </cl-list-item>
|
||||
</cl-list>
|
||||
</view>
|
||||
|
||||
<cl-sticky :offset-top="40">
|
||||
<view class="bg-red-500 p-3 h-[40px] flex flex-row items-center">
|
||||
<cl-text color="white">Header - 2</cl-text>
|
||||
</view>
|
||||
</cl-sticky>
|
||||
|
||||
<view class="p-3">
|
||||
<cl-list>
|
||||
<cl-list-item :label="`${i}`" v-for="i in 50" :key="i"> </cl-list-item>
|
||||
</cl-list>
|
||||
</view>
|
||||
|
||||
<cl-sticky :offset-top="80">
|
||||
<view class="bg-purple-500 p-3 h-[40px] flex flex-row items-center">
|
||||
<cl-text color="white">Header - 3</cl-text>
|
||||
</view>
|
||||
</cl-sticky>
|
||||
|
||||
<view class="p-3">
|
||||
<cl-list>
|
||||
<cl-list-item :label="`${i}`" v-for="i in 50" :key="i"> </cl-list-item>
|
||||
</cl-list>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
</script>
|
||||
170
cool-unix/pages/demo/layout/tabs.uvue
Normal file
170
cool-unix/pages/demo/layout/tabs.uvue
Normal file
@@ -0,0 +1,170 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-tabs v-model="form.val1" :list="list"></cl-tabs>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('显示滑块')">
|
||||
<cl-tabs
|
||||
v-model="form.val2"
|
||||
:list="list"
|
||||
show-slider
|
||||
:pt="{ className: isPad ? '!p-2' : '' }"
|
||||
></cl-tabs>
|
||||
|
||||
<cl-list border :pt="{ className: 'mt-3' }">
|
||||
<cl-list-item :label="t('添加间距')">
|
||||
<cl-switch v-model="isPad"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('横向填充')">
|
||||
<demo-tips>{{ t("适用于标签数量不多的情况") }}</demo-tips>
|
||||
|
||||
<cl-tabs v-model="form.val3" :list="list2" fill></cl-tabs>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('居中')">
|
||||
<view class="flex flex-row justify-center">
|
||||
<cl-tabs v-model="form.val4" :list="list2"></cl-tabs>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('单个禁用')">
|
||||
<cl-tabs v-model="form.val5" :list="list3"></cl-tabs>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义')">
|
||||
<cl-tabs
|
||||
v-model="form.val6"
|
||||
:list="list"
|
||||
:show-line="isShowLine"
|
||||
:disabled="isDisabled"
|
||||
:color="isColor ? 'red' : ''"
|
||||
:un-color="isColor ? '#ccc' : ''"
|
||||
></cl-tabs>
|
||||
|
||||
<cl-list border :pt="{ className: 'mt-3' }">
|
||||
<cl-list-item :label="t('显示下划线')">
|
||||
<cl-switch v-model="isShowLine"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('禁用')">
|
||||
<cl-switch v-model="isDisabled"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('自定义颜色')">
|
||||
<cl-switch v-model="isColor"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import DemoTips from "../components/tips.uvue";
|
||||
import { reactive, ref } from "vue";
|
||||
import type { ClTabsItem } from "@/uni_modules/cool-ui";
|
||||
|
||||
type Form = {
|
||||
val1: string;
|
||||
val2: string;
|
||||
val3: string;
|
||||
val4: string;
|
||||
val5: string;
|
||||
val6: string;
|
||||
};
|
||||
|
||||
const form = reactive<Form>({
|
||||
val1: "1",
|
||||
val2: "2",
|
||||
val3: "1",
|
||||
val4: "1",
|
||||
val5: "2",
|
||||
val6: "1"
|
||||
});
|
||||
|
||||
const list = ref<ClTabsItem[]>([
|
||||
{
|
||||
label: "Vue",
|
||||
value: "1"
|
||||
},
|
||||
{
|
||||
label: "React",
|
||||
value: "2"
|
||||
},
|
||||
{
|
||||
label: "Angular",
|
||||
value: "3"
|
||||
},
|
||||
{
|
||||
label: "Svelte",
|
||||
value: "4"
|
||||
},
|
||||
{
|
||||
label: "Jquery",
|
||||
value: "5"
|
||||
},
|
||||
{
|
||||
label: "Vuex",
|
||||
value: "6"
|
||||
},
|
||||
{
|
||||
label: "Vue Router",
|
||||
value: "7"
|
||||
},
|
||||
{
|
||||
label: "Pinia",
|
||||
value: "8"
|
||||
}
|
||||
]);
|
||||
|
||||
const list2 = ref<ClTabsItem[]>([
|
||||
{
|
||||
label: "Vue",
|
||||
value: "1"
|
||||
},
|
||||
{
|
||||
label: "React",
|
||||
value: "2"
|
||||
},
|
||||
{
|
||||
label: "Angular",
|
||||
value: "3"
|
||||
},
|
||||
{
|
||||
label: "Svelte",
|
||||
value: "4"
|
||||
}
|
||||
]);
|
||||
|
||||
const list3 = ref<ClTabsItem[]>([
|
||||
{
|
||||
label: "Vue",
|
||||
value: "1"
|
||||
},
|
||||
{
|
||||
label: "React",
|
||||
value: "2"
|
||||
},
|
||||
{
|
||||
label: "Angular",
|
||||
value: "3",
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
label: "Svelte",
|
||||
value: "4"
|
||||
}
|
||||
]);
|
||||
|
||||
const isShowLine = ref(true);
|
||||
const isDisabled = ref(false);
|
||||
const isColor = ref(false);
|
||||
const isPad = ref(false);
|
||||
</script>
|
||||
97
cool-unix/pages/demo/layout/topbar.uvue
Normal file
97
cool-unix/pages/demo/layout/topbar.uvue
Normal file
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-topbar :title="t('标题')"> </cl-topbar>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('插槽')">
|
||||
<cl-topbar :title="t('标题')">
|
||||
<template #prepend>
|
||||
<cl-icon
|
||||
name="heart-fill"
|
||||
:size="38"
|
||||
:pt="{
|
||||
className: 'ml-2'
|
||||
}"
|
||||
></cl-icon>
|
||||
</template>
|
||||
|
||||
<template #append>
|
||||
<cl-icon name="search-line" :size="38"></cl-icon>
|
||||
</template>
|
||||
</cl-topbar>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义颜色')">
|
||||
<cl-topbar :title="t('标题')" color="white" background-color="#409EFF"> </cl-topbar>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('使用 PT 自定义颜色')">
|
||||
<cl-topbar
|
||||
:title="t('标题')"
|
||||
:pt="{
|
||||
className: 'bg-surface-800',
|
||||
title: {
|
||||
className: 'text-white'
|
||||
},
|
||||
back: {
|
||||
className: 'text-white'
|
||||
}
|
||||
}"
|
||||
>
|
||||
</cl-topbar>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义返回图标')">
|
||||
<cl-topbar
|
||||
:title="t('标题')"
|
||||
back-icon="home-2-line"
|
||||
:pt="{
|
||||
back: {
|
||||
size: 38
|
||||
}
|
||||
}"
|
||||
>
|
||||
</cl-topbar>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('禁用返回按钮')">
|
||||
<cl-topbar :title="t('标题')" :backable="false"> </cl-topbar>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义返回路径')">
|
||||
<cl-topbar :title="t('标题')" back-path="/pages/user/login">
|
||||
<template #prepend>
|
||||
<cl-text>{{ t("登录") }}</cl-text>
|
||||
</template>
|
||||
</cl-topbar>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义标题内容')">
|
||||
<cl-topbar>
|
||||
<cl-tabs v-model="type" :height="66" :list="typeList"></cl-tabs>
|
||||
</cl-topbar>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { ref } from "vue";
|
||||
import type { ClTabsItem } from "@/uni_modules/cool-ui";
|
||||
|
||||
const type = ref("fans");
|
||||
const typeList = ref<ClTabsItem[]>([
|
||||
{
|
||||
label: "我的粉丝",
|
||||
value: "fans"
|
||||
},
|
||||
{
|
||||
label: "我的关注",
|
||||
value: "follow"
|
||||
}
|
||||
]);
|
||||
</script>
|
||||
283
cool-unix/pages/demo/other/animation.uvue
Normal file
283
cool-unix/pages/demo/other/animation.uvue
Normal file
@@ -0,0 +1,283 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础动画')">
|
||||
<view class="row">
|
||||
<view class="item" ref="rotateRef">
|
||||
<cl-text color="white" :pt="{ className: 'text-sm' }">rotate</cl-text>
|
||||
</view>
|
||||
<view class="item" ref="scaleRef">
|
||||
<cl-text color="white" :pt="{ className: 'text-sm' }">scale</cl-text>
|
||||
</view>
|
||||
<view class="item" ref="moveRef">
|
||||
<cl-text color="white" :pt="{ className: 'text-sm' }">move</cl-text>
|
||||
</view>
|
||||
<view class="item" ref="opacityRef">
|
||||
<cl-text color="white" :pt="{ className: 'text-sm' }">opacity</cl-text>
|
||||
</view>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('淡入淡出')">
|
||||
<view class="row">
|
||||
<view class="item" ref="fadeInRef">
|
||||
<cl-text color="white" :pt="{ className: 'text-sm' }">fadeIn</cl-text>
|
||||
</view>
|
||||
<view class="item" ref="fadeOutRef">
|
||||
<cl-text color="white" :pt="{ className: 'text-sm' }">fadeOut</cl-text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<cl-button text border @tap="onFadeToggle">{{ t("播放动画") }}</cl-button>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('滑入')">
|
||||
<view class="row">
|
||||
<view class="item" ref="slideLeftRef">
|
||||
<cl-text color="white" :pt="{ className: 'text-sm' }">slideLeft</cl-text>
|
||||
</view>
|
||||
<view class="item" ref="slideRightRef">
|
||||
<cl-text color="white" :pt="{ className: 'text-sm' }">slideRight</cl-text>
|
||||
</view>
|
||||
<view class="item" ref="slideUpRef">
|
||||
<cl-text color="white" :pt="{ className: 'text-sm' }">slideUp</cl-text>
|
||||
</view>
|
||||
<view class="item" ref="slideDownRef">
|
||||
<cl-text color="white" :pt="{ className: 'text-sm' }">slideDown</cl-text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<cl-button text border @tap="onSlideInToggle">{{ t("播放动画") }}</cl-button>
|
||||
</demo-item>
|
||||
|
||||
<demo-item label="缩放">
|
||||
<view class="row">
|
||||
<view class="item" ref="zoomInRef">
|
||||
<cl-text color="white" :pt="{ className: 'text-sm' }">zoomIn</cl-text>
|
||||
</view>
|
||||
<view class="item" ref="zoomOutRef">
|
||||
<cl-text color="white" :pt="{ className: 'text-sm' }">zoomOut</cl-text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<cl-button text border @tap="onZoomToggle">{{ t("播放动画") }}</cl-button>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('旋转翻转')">
|
||||
<view class="row">
|
||||
<view class="item" ref="rotateInRef">
|
||||
<cl-text color="white" :pt="{ className: 'text-sm' }">rotateIn</cl-text>
|
||||
</view>
|
||||
<view class="item" ref="flipXRef">
|
||||
<cl-text color="white" :pt="{ className: 'text-sm' }">flipX</cl-text>
|
||||
</view>
|
||||
<view class="item" ref="flipYRef">
|
||||
<cl-text color="white" :pt="{ className: 'text-sm' }">flipY</cl-text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<cl-button text border @tap="onRotateFlipToggle">{{ t("播放动画") }}</cl-button>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('摇摆抖动')">
|
||||
<view class="row">
|
||||
<view class="item" ref="shakeRef">
|
||||
<cl-text color="white" :pt="{ className: 'text-sm' }">shake</cl-text>
|
||||
</view>
|
||||
<view class="item" ref="swingRef">
|
||||
<cl-text color="white" :pt="{ className: 'text-sm' }">swing</cl-text>
|
||||
</view>
|
||||
<view class="item" ref="wobbleRef">
|
||||
<cl-text color="white" :pt="{ className: 'text-sm' }">wobble</cl-text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<cl-button text border @tap="onShakeToggle">{{ t("播放动画") }}</cl-button>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('特殊效果')">
|
||||
<view class="row">
|
||||
<view class="item" ref="rollInRef">
|
||||
<cl-text color="white" :pt="{ className: 'text-sm' }">rollIn</cl-text>
|
||||
</view>
|
||||
<view class="item" ref="lightSpeedRef">
|
||||
<cl-text color="white" :pt="{ className: 'text-sm' }">lightSpeed</cl-text>
|
||||
</view>
|
||||
<view class="item" ref="rippleRef">
|
||||
<cl-text color="white" :pt="{ className: 'text-sm' }">ripple</cl-text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<cl-button text border @tap="onSpecialToggle">{{ t("播放动画") }}</cl-button>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('组合动画')">
|
||||
<view class="row">
|
||||
<view class="item" ref="sequenceRef">
|
||||
<cl-text color="white" :pt="{ className: 'text-sm' }">sequence</cl-text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<cl-button text border @tap="onSequenceToggle">{{ t("播放动画") }}</cl-button>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { AnimationEngine, createAnimation } from "@/cool";
|
||||
import { ref } from "vue";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { t } from "@/locale";
|
||||
|
||||
// 基础动画引用
|
||||
const rotateRef = ref<UniElement | null>(null);
|
||||
const moveRef = ref<UniElement | null>(null);
|
||||
const scaleRef = ref<UniElement | null>(null);
|
||||
const opacityRef = ref<UniElement | null>(null);
|
||||
|
||||
// 淡入淡出动画引用
|
||||
const fadeInRef = ref<UniElement | null>(null);
|
||||
const fadeOutRef = ref<UniElement | null>(null);
|
||||
|
||||
// 滑入动画引用
|
||||
const slideLeftRef = ref<UniElement | null>(null);
|
||||
const slideRightRef = ref<UniElement | null>(null);
|
||||
const slideUpRef = ref<UniElement | null>(null);
|
||||
const slideDownRef = ref<UniElement | null>(null);
|
||||
|
||||
// 缩放动画引用
|
||||
const zoomInRef = ref<UniElement | null>(null);
|
||||
const zoomOutRef = ref<UniElement | null>(null);
|
||||
|
||||
// 旋转翻转动画引用
|
||||
const rotateInRef = ref<UniElement | null>(null);
|
||||
const flipXRef = ref<UniElement | null>(null);
|
||||
const flipYRef = ref<UniElement | null>(null);
|
||||
|
||||
// 摇摆抖动动画引用
|
||||
const shakeRef = ref<UniElement | null>(null);
|
||||
const swingRef = ref<UniElement | null>(null);
|
||||
const wobbleRef = ref<UniElement | null>(null);
|
||||
|
||||
// 特殊效果动画引用
|
||||
const rollInRef = ref<UniElement | null>(null);
|
||||
const lightSpeedRef = ref<UniElement | null>(null);
|
||||
const rippleRef = ref<UniElement | null>(null);
|
||||
|
||||
// 组合动画引用
|
||||
const sequenceRef = ref<UniElement | null>(null);
|
||||
|
||||
// 基础动画方法
|
||||
function onRotate() {
|
||||
createAnimation(rotateRef.value, {
|
||||
duration: 1000,
|
||||
loop: -1
|
||||
})
|
||||
.rotate("0deg", "360deg")
|
||||
.play();
|
||||
}
|
||||
|
||||
function onMove() {
|
||||
createAnimation(moveRef.value, {
|
||||
duration: 1000,
|
||||
loop: -1,
|
||||
alternate: true
|
||||
})
|
||||
.translateX("0rpx", "300rpx")
|
||||
.play();
|
||||
}
|
||||
|
||||
function onScale() {
|
||||
createAnimation(scaleRef.value, {
|
||||
duration: 500,
|
||||
loop: -1,
|
||||
alternate: true
|
||||
})
|
||||
.scale("1", "1.2")
|
||||
.play();
|
||||
}
|
||||
|
||||
function onOpacity() {
|
||||
createAnimation(opacityRef.value, {
|
||||
duration: 500,
|
||||
loop: -1,
|
||||
alternate: true
|
||||
})
|
||||
.opacity("1", "0.5")
|
||||
.play();
|
||||
}
|
||||
|
||||
// 淡入淡出动画方法
|
||||
function onFadeToggle() {
|
||||
createAnimation(fadeInRef.value).fadeIn(500).play();
|
||||
createAnimation(fadeOutRef.value).fadeOut(500).play();
|
||||
}
|
||||
|
||||
// 滑入动画方法
|
||||
function onSlideInToggle() {
|
||||
createAnimation(slideLeftRef.value).slideInLeft(400).play();
|
||||
createAnimation(slideRightRef.value).slideInRight(400).play();
|
||||
createAnimation(slideUpRef.value).slideInUp(400).play();
|
||||
createAnimation(slideDownRef.value).slideInDown(400).play();
|
||||
}
|
||||
|
||||
// 缩放动画方法
|
||||
function onZoomToggle() {
|
||||
createAnimation(zoomInRef.value).zoomIn(400).play();
|
||||
createAnimation(zoomOutRef.value).zoomOut(400).play();
|
||||
}
|
||||
|
||||
// 旋转翻转动画方法
|
||||
function onRotateFlipToggle() {
|
||||
createAnimation(rotateInRef.value).rotateIn(600).play();
|
||||
createAnimation(flipXRef.value).flipX(600).play();
|
||||
createAnimation(flipYRef.value).flipY(600).play();
|
||||
}
|
||||
|
||||
// 摇摆抖动动画方法
|
||||
function onShakeToggle() {
|
||||
createAnimation(shakeRef.value).shake(200).play();
|
||||
createAnimation(swingRef.value).swing(200).play();
|
||||
createAnimation(wobbleRef.value).wobble(200).play();
|
||||
}
|
||||
|
||||
// 特殊效果动画方法
|
||||
function onSpecialToggle() {
|
||||
createAnimation(rollInRef.value).rollIn(600).play();
|
||||
createAnimation(lightSpeedRef.value).lightSpeed(500).play();
|
||||
createAnimation(rippleRef.value).ripple(600).play();
|
||||
}
|
||||
|
||||
// 异步序列动画方法
|
||||
async function onSequenceToggle() {
|
||||
createAnimation(sequenceRef.value)
|
||||
.sequence([
|
||||
(engine: AnimationEngine) => engine.fadeIn(300),
|
||||
(engine: AnimationEngine) => engine.scale("1", "1.5").setDuration(400),
|
||||
(engine: AnimationEngine) => engine.rotate("0deg", "360deg").setDuration(500),
|
||||
(engine: AnimationEngine) => engine.scale("1.5", "1").setDuration(300),
|
||||
(engine: AnimationEngine) => engine.fadeOut(300)
|
||||
])
|
||||
.play();
|
||||
}
|
||||
|
||||
onReady(() => {
|
||||
onRotate();
|
||||
onMove();
|
||||
onScale();
|
||||
onOpacity();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.row {
|
||||
@apply flex flex-row justify-between items-center overflow-visible;
|
||||
margin: 0 -10rpx 20rpx -10rpx;
|
||||
|
||||
.item {
|
||||
@apply flex items-center justify-center h-16 bg-primary-500 rounded-xl flex-1;
|
||||
margin: 0 10rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
233
cool-unix/pages/demo/other/canvas.uvue
Normal file
233
cool-unix/pages/demo/other/canvas.uvue
Normal file
@@ -0,0 +1,233 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<cl-canvas
|
||||
v-if="width > 0"
|
||||
ref="canvasRef"
|
||||
canvas-id="test"
|
||||
:height="height"
|
||||
:width="width"
|
||||
@load="onCanvasLoad"
|
||||
></cl-canvas>
|
||||
|
||||
<cl-footer>
|
||||
<!-- #ifdef H5 -->
|
||||
<cl-button type="primary" @click="previewImage">{{ t("预览图片") }}</cl-button>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- #ifndef H5 -->
|
||||
<cl-button type="primary" @click="saveImage">{{ t("保存图片") }}</cl-button>
|
||||
<!-- #endif -->
|
||||
</cl-footer>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import { Canvas } from "@/uni_modules/cool-canvas";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
import { ref } from "vue";
|
||||
|
||||
const ui = useUi();
|
||||
|
||||
const canvasRef = ref<ClCanvasComponentPublicInstance | null>(null);
|
||||
|
||||
// 初始化为 0,避免页面未完全渲染时 getWindowInfo 获取到的高度或宽度不准确(如已知固定高宽可直接赋值)
|
||||
const height = ref(0);
|
||||
const width = ref(0);
|
||||
|
||||
function onCanvasLoad(canvas: Canvas) {
|
||||
canvas
|
||||
.image({
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: width.value,
|
||||
height: height.value,
|
||||
url: "/static/demo/canvas/bg.png"
|
||||
})
|
||||
.image({
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: width.value,
|
||||
height: height.value,
|
||||
url: "/static/demo/canvas/light.png"
|
||||
})
|
||||
.image({
|
||||
x: (width.value - 226) / 2,
|
||||
y: 60,
|
||||
width: 226,
|
||||
height: 77,
|
||||
url: "/static/demo/canvas/text-yqhy.png"
|
||||
})
|
||||
.image({
|
||||
x: (width.value - 325) / 2,
|
||||
y: 125,
|
||||
width: 325,
|
||||
height: 77,
|
||||
url: "/static/demo/canvas/text-dezk.png"
|
||||
})
|
||||
.image({
|
||||
x: (width.value - 196) / 2,
|
||||
y: 190,
|
||||
width: 196,
|
||||
height: 62,
|
||||
url: "/static/demo/canvas/text-xrfl.png"
|
||||
})
|
||||
.image({
|
||||
x: (width.value - 374) / 2 - 1,
|
||||
y: 500,
|
||||
width: 374,
|
||||
height: 220,
|
||||
url: "/static/demo/canvas/rp-t.png"
|
||||
})
|
||||
.image({
|
||||
x: (width.value - 327) / 2,
|
||||
y: 280,
|
||||
width: 327,
|
||||
height: 430,
|
||||
url: "/static/demo/canvas/bg-content.png"
|
||||
})
|
||||
.image({
|
||||
x: 30,
|
||||
y: 240,
|
||||
width: 114,
|
||||
height: 120,
|
||||
url: "/static/demo/canvas/gold-l.png"
|
||||
})
|
||||
.image({
|
||||
x: width.value - 106 - 50,
|
||||
y: 240,
|
||||
width: 106,
|
||||
height: 107,
|
||||
url: "/static/demo/canvas/gold-r.png"
|
||||
})
|
||||
.image({
|
||||
x: (width.value - 350) / 2,
|
||||
y: 595,
|
||||
width: 350,
|
||||
height: 122,
|
||||
url: "/static/demo/canvas/rp-b.png"
|
||||
})
|
||||
.image({
|
||||
x: (width.value - 208) / 2,
|
||||
y: 631,
|
||||
width: 208,
|
||||
height: 89,
|
||||
url: "/static/demo/canvas/invite-btn.png"
|
||||
})
|
||||
.div({
|
||||
x: (width.value - 276) / 2,
|
||||
y: 335,
|
||||
width: 276,
|
||||
height: 210,
|
||||
backgroundColor: "#fff",
|
||||
radius: 10
|
||||
})
|
||||
.text({
|
||||
x: 0,
|
||||
y: 350,
|
||||
width: width.value,
|
||||
content: "新人专享",
|
||||
color: "#F73035",
|
||||
textAlign: "center",
|
||||
fontSize: 28,
|
||||
fontWeight: "bold"
|
||||
})
|
||||
.text({
|
||||
x: 0,
|
||||
y: 390,
|
||||
width: width.value,
|
||||
content: "限时领券30元",
|
||||
color: "#F73035",
|
||||
textAlign: "center",
|
||||
fontSize: 16
|
||||
})
|
||||
.image({
|
||||
x: (width.value - 246) / 2,
|
||||
y: 432,
|
||||
width: 246,
|
||||
height: 98,
|
||||
url: "/static/demo/canvas/coupon.png"
|
||||
})
|
||||
.text({
|
||||
x: (width.value - 246) / 2,
|
||||
y: 435,
|
||||
content: "领券",
|
||||
width: 34,
|
||||
color: "#fff",
|
||||
fontSize: 11,
|
||||
textAlign: "center"
|
||||
})
|
||||
.text({
|
||||
x: (width.value - 246) / 2,
|
||||
y: 454,
|
||||
width: 86,
|
||||
content: "80",
|
||||
color: "#604E44",
|
||||
fontSize: 46,
|
||||
textAlign: "center",
|
||||
fontWeight: "bold"
|
||||
})
|
||||
.text({
|
||||
x: (width.value - 246) / 2 + 86,
|
||||
y: 459,
|
||||
width: 246 - 86,
|
||||
content: "新人专享优惠券",
|
||||
color: "#604E44",
|
||||
fontSize: 18,
|
||||
textAlign: "center"
|
||||
})
|
||||
.text({
|
||||
x: (width.value - 246) / 2 + 86,
|
||||
y: 489,
|
||||
width: 246 - 86,
|
||||
content: "2021.04.30-2021.06.30",
|
||||
color: "#604E44",
|
||||
fontSize: 12,
|
||||
textAlign: "center"
|
||||
})
|
||||
.text({
|
||||
x: 0,
|
||||
y: 560,
|
||||
width: width.value,
|
||||
content: "邀请好友,双方均可获得20元优惠券",
|
||||
color: "#756056",
|
||||
fontSize: 15,
|
||||
textAlign: "center"
|
||||
})
|
||||
.text({
|
||||
x: 0,
|
||||
y: 300,
|
||||
width: width.value,
|
||||
content: "~ 专属新人福利 ~",
|
||||
color: "#956056",
|
||||
textAlign: "center",
|
||||
opacity: 0.7
|
||||
})
|
||||
.draw();
|
||||
}
|
||||
|
||||
function previewImage() {
|
||||
canvasRef.value!.previewImage();
|
||||
}
|
||||
|
||||
function saveImage() {
|
||||
canvasRef.value!.saveImage();
|
||||
}
|
||||
|
||||
onReady(() => {
|
||||
// 同上
|
||||
height.value = uni.getWindowInfo().windowHeight;
|
||||
width.value = uni.getWindowInfo().windowWidth;
|
||||
|
||||
ui.showConfirm({
|
||||
title: t("提示"),
|
||||
message: t("本页面内容由 canvas 渲染生成,是否立即预览图片效果?"),
|
||||
confirmText: t("预览"),
|
||||
callback(action) {
|
||||
if (action == "confirm") {
|
||||
canvasRef.value!.previewImage();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
55
cool-unix/pages/demo/other/cropper.uvue
Normal file
55
cool-unix/pages/demo/other/cropper.uvue
Normal file
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('自定义')">
|
||||
<cl-button @tap="chooseImage">{{ t("选择图片") }}</cl-button>
|
||||
|
||||
<cl-list border :pt="{ className: 'mt-5' }">
|
||||
<cl-list-item :label="t('可调节裁剪框大小')">
|
||||
<cl-switch v-model="resizable"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
|
||||
<cl-cropper
|
||||
ref="cropperRef"
|
||||
:resizable="resizable"
|
||||
@crop="onCrop"
|
||||
@load="onImageLoad"
|
||||
></cl-cropper>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { ref } from "vue";
|
||||
|
||||
const cropperRef = ref<ClCropperComponentPublicInstance | null>(null);
|
||||
|
||||
const resizable = ref(true);
|
||||
|
||||
function chooseImage() {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
sizeType: ["original", "compressed"],
|
||||
sourceType: ["album", "camera"],
|
||||
success: (res) => {
|
||||
if (res.tempFilePaths.length > 0) {
|
||||
cropperRef.value!.open(res.tempFilePaths[0]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onCrop(url: string) {
|
||||
uni.previewImage({
|
||||
urls: [url]
|
||||
});
|
||||
}
|
||||
|
||||
function onImageLoad(e: UniImageLoadEvent) {
|
||||
console.log("onImageLoad", e);
|
||||
}
|
||||
</script>
|
||||
50
cool-unix/pages/demo/other/day-uts.uvue
Normal file
50
cool-unix/pages/demo/other/day-uts.uvue
Normal file
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('格式化')">
|
||||
<cl-text>format("YYYY-MM-DD HH:mm:ss")</cl-text>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('添加')">
|
||||
<cl-text>add(1, "day")</cl-text>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('减去')">
|
||||
<cl-text>subtract(1, "day")</cl-text>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('获取某个单位的开始时间')">
|
||||
<cl-text>startOf("day")</cl-text>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('获取某个单位的结束时间')">
|
||||
<cl-text>endOf("month")</cl-text>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('是否同一天')">
|
||||
<cl-text>isSame(Date)</cl-text>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('是否早于')">
|
||||
<cl-text>isBefore(Date)</cl-text>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('是否晚于')">
|
||||
<cl-text>isAfter(Date)</cl-text>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('差值')">
|
||||
<cl-text>diff(Date)</cl-text>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('差值(单位)')">
|
||||
<cl-text>diffUnit(Date, "day")</cl-text>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
</script>
|
||||
88
cool-unix/pages/demo/other/qrcode.uvue
Normal file
88
cool-unix/pages/demo/other/qrcode.uvue
Normal file
@@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('自定义')">
|
||||
<view class="flex flex-row justify-center py-3">
|
||||
<cl-qrcode
|
||||
ref="qrcodeRef"
|
||||
text="https://cool-js.com/"
|
||||
:logo="isLogo ? 'https://unix.cool-js.com/logo.png' : ''"
|
||||
:pd-radius="isPdRadius ? 50 : 0"
|
||||
:padding="isPadding ? 10 : 5"
|
||||
:foreground="isColor ? '#14b8a6' : '#000000'"
|
||||
:pd-color="isColor ? '#0d9488' : '#000000'"
|
||||
:mode="mode"
|
||||
></cl-qrcode>
|
||||
</view>
|
||||
|
||||
<cl-list border class="mt-3">
|
||||
<cl-list-item :label="t('添加LOGO')">
|
||||
<cl-switch v-model="isLogo"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('圆角定位点')">
|
||||
<cl-switch v-model="isPdRadius"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('内间距')">
|
||||
<cl-switch v-model="isPadding"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('自定义颜色')">
|
||||
<cl-switch v-model="isColor"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('导出图片')">
|
||||
<cl-button @tap="save">{{ t("预览") }}</cl-button>
|
||||
</cl-list-item>
|
||||
|
||||
<view class="p-2">
|
||||
<cl-tabs v-model="mode" :height="66" :list="modeList" show-slider></cl-tabs>
|
||||
</view>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { ClQrcodeMode, ClTabsItem } from "@/uni_modules/cool-ui";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { ref } from "vue";
|
||||
import { t } from "@/locale";
|
||||
|
||||
const isLogo = ref(true);
|
||||
const isPdRadius = ref(true);
|
||||
const isPadding = ref(false);
|
||||
const isColor = ref(false);
|
||||
|
||||
const qrcodeRef = ref<ClQrcodeComponentPublicInstance | null>(null);
|
||||
|
||||
const mode = ref<ClQrcodeMode>("circular");
|
||||
const modeList = [
|
||||
{
|
||||
label: t("矩形"),
|
||||
value: "rect"
|
||||
},
|
||||
{
|
||||
label: t("点"),
|
||||
value: "circular"
|
||||
},
|
||||
{
|
||||
label: t("线性"),
|
||||
value: "line"
|
||||
},
|
||||
{
|
||||
label: t("小方格"),
|
||||
value: "rectSmall"
|
||||
}
|
||||
] as ClTabsItem[];
|
||||
|
||||
function save() {
|
||||
qrcodeRef.value!.toPng().then((url) => {
|
||||
uni.previewImage({
|
||||
urls: [url]
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
41
cool-unix/pages/demo/other/router/index.uvue
Normal file
41
cool-unix/pages/demo/other/router/index.uvue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('跳转')">
|
||||
<cl-button @click="toPush">{{ t("跳转") }}</cl-button>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('带参数')">
|
||||
<cl-button @click="toQuery">{{ t("跳转") }}</cl-button>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('需登录')">
|
||||
<cl-button @click="toLogin">{{ t("跳转") }}</cl-button>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { router, uuid } from "@/cool";
|
||||
import DemoItem from "../../components/item.uvue";
|
||||
import { t } from "@/locale";
|
||||
|
||||
function toPush() {
|
||||
router.to("/pages/demo/other/router/query");
|
||||
}
|
||||
|
||||
function toQuery() {
|
||||
router.push({ path: "/pages/demo/other/router/query", query: { id: uuid() } });
|
||||
}
|
||||
|
||||
function toLogin() {
|
||||
router.push({
|
||||
path: "/pages/demo/other/router/query",
|
||||
query: { id: uuid() },
|
||||
isAuth: true
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
34
cool-unix/pages/demo/other/router/query.uvue
Normal file
34
cool-unix/pages/demo/other/router/query.uvue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('ID')">
|
||||
<cl-text>{{ id ?? "-" }}</cl-text>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('用户信息')" v-if="!user.isNull()">
|
||||
<cl-text>{{ userInfo?.nickName ?? "-" }}</cl-text>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../../components/item.uvue";
|
||||
import { router, userInfo, useStore } from "@/cool";
|
||||
|
||||
const { user } = useStore();
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: String
|
||||
}
|
||||
});
|
||||
|
||||
onReady(() => {
|
||||
const query = router.query();
|
||||
console.log(query);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
83
cool-unix/pages/demo/other/share.uvue
Normal file
83
cool-unix/pages/demo/other/share.uvue
Normal file
@@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('分享文本')">
|
||||
<cl-button @tap="shareText">{{ t("分享文本") }}</cl-button>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('分享图片')">
|
||||
<cl-button @tap="shareImage">{{ t("分享图片") }}</cl-button>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('分享文件')">
|
||||
<cl-button @tap="shareFile">{{ t("分享文件") }}</cl-button>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('分享链接')">
|
||||
<cl-button @tap="shareLink">{{ t("分享链接") }}</cl-button>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
// #ifdef APP
|
||||
import { shareWithSystem } from "@/uni_modules/cool-share";
|
||||
// #endif
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { t } from "@/locale";
|
||||
|
||||
function shareText() {
|
||||
// #ifdef APP
|
||||
shareWithSystem({
|
||||
type: "text",
|
||||
title: "Cool Unix",
|
||||
summary: "Cool Unix 是一个高效的项目脚手架",
|
||||
success: () => {
|
||||
console.log("success");
|
||||
},
|
||||
fail: (error) => {
|
||||
console.log("fail", error);
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
}
|
||||
|
||||
function shareImage() {
|
||||
// #ifdef APP
|
||||
shareWithSystem({
|
||||
type: "image",
|
||||
url: "https://cool-js.com/logo.png",
|
||||
success: () => {
|
||||
console.log("success");
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
}
|
||||
|
||||
function shareFile() {
|
||||
// #ifdef APP
|
||||
shareWithSystem({
|
||||
type: "file",
|
||||
url: "https://show.cool-admin.com/用户导入模版.xlsx",
|
||||
success: () => {
|
||||
console.log("success");
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
}
|
||||
|
||||
function shareLink() {
|
||||
// #ifdef APP
|
||||
shareWithSystem({
|
||||
type: "link",
|
||||
url: "https://cool-js.com/",
|
||||
success: () => {
|
||||
console.log("success");
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
54
cool-unix/pages/demo/other/sign.uvue
Normal file
54
cool-unix/pages/demo/other/sign.uvue
Normal file
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<cl-sign ref="signRef" :height="height" :width="width" :enable-brush="isBrush"></cl-sign>
|
||||
|
||||
<view class="p-3">
|
||||
<cl-list>
|
||||
<cl-list-item :label="t('操作')">
|
||||
<cl-button type="info" @click="clear">{{ t("清空") }}</cl-button>
|
||||
<cl-button @click="preview">{{ t("预览") }}</cl-button>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('设置高度')">
|
||||
<cl-switch v-model="isFullscreen" @change="onFullscreenChange"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('毛笔效果')">
|
||||
<cl-switch v-model="isBrush"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { t } from "@/locale";
|
||||
import { ref } from "vue";
|
||||
|
||||
const height = ref(200);
|
||||
const width = ref(0);
|
||||
|
||||
const isFullscreen = ref(false);
|
||||
const isBrush = ref(true);
|
||||
const signRef = ref<ClSignComponentPublicInstance | null>(null);
|
||||
|
||||
function clear() {
|
||||
signRef.value!.clear();
|
||||
}
|
||||
|
||||
function preview() {
|
||||
signRef.value!.toPng().then((url) => {
|
||||
uni.previewImage({
|
||||
urls: [url]
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function onFullscreenChange() {
|
||||
height.value = isFullscreen.value ? uni.getWindowInfo().windowHeight - 200 : 200;
|
||||
}
|
||||
|
||||
onReady(() => {
|
||||
width.value = uni.getWindowInfo().windowWidth;
|
||||
});
|
||||
</script>
|
||||
47
cool-unix/pages/demo/other/slide-verify.uvue
Normal file
47
cool-unix/pages/demo/other/slide-verify.uvue
Normal file
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-slide-verify
|
||||
v-model="status"
|
||||
@success="onSuccess"
|
||||
@fail="onFail"
|
||||
></cl-slide-verify>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('没有错误提示')">
|
||||
<cl-slide-verify :show-fail="false"></cl-slide-verify>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('转动图片')">
|
||||
<cl-slide-verify
|
||||
mode="image"
|
||||
image-url="https://unix.cool-js.com/images/demo/avatar.jpg"
|
||||
></cl-slide-verify>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { t } from "@/locale";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
|
||||
const ui = useUi();
|
||||
|
||||
const status = ref(false);
|
||||
|
||||
function onSuccess() {
|
||||
ui.showToast({
|
||||
message: t("验证通过")
|
||||
});
|
||||
}
|
||||
|
||||
function onFail() {
|
||||
ui.showToast({
|
||||
message: t("验证失败")
|
||||
});
|
||||
}
|
||||
</script>
|
||||
71
cool-unix/pages/demo/other/svg.uvue
Normal file
71
cool-unix/pages/demo/other/svg.uvue
Normal file
@@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<view class="flex flex-row">
|
||||
<cl-svg src="/static/demo/svg/category.svg" class="h-6 w-6"></cl-svg>
|
||||
<cl-svg src="/static/demo/svg/shopping-cart.svg" class="h-6 w-6 ml-3"></cl-svg>
|
||||
<cl-svg src="/static/demo/svg/points.svg" class="h-6 w-6 ml-3"></cl-svg>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('不同大小')">
|
||||
<view class="flex flex-row">
|
||||
<cl-svg src="/static/demo/svg/points.svg" class="h-10 w-10"></cl-svg>
|
||||
<cl-svg src="/static/demo/svg/points.svg" class="h-8 w-8 ml-3"></cl-svg>
|
||||
<cl-svg src="/static/demo/svg/points.svg" class="h-6 w-6 ml-3"></cl-svg>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('不同颜色')">
|
||||
<view class="flex flex-row">
|
||||
<!-- #ifdef MP -->
|
||||
<cl-svg :src="svg" color="primary" class="h-6 w-6"></cl-svg>
|
||||
|
||||
<cl-svg :src="svg" color="#d946ef" class="h-6 w-6 ml-3"></cl-svg>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- #ifndef MP -->
|
||||
<cl-svg
|
||||
src="/static/demo/svg/category.svg"
|
||||
color="primary"
|
||||
class="h-6 w-6"
|
||||
></cl-svg>
|
||||
<cl-svg
|
||||
src="/static/demo/svg/shopping-cart.svg"
|
||||
color="#eab308"
|
||||
class="h-6 w-6 ml-3"
|
||||
></cl-svg>
|
||||
<cl-svg
|
||||
src="/static/demo/svg/points.svg"
|
||||
color="#a855f7"
|
||||
class="h-6 w-6 ml-3"
|
||||
></cl-svg>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<!-- #ifdef APP-ANDROID || H5 -->
|
||||
<demo-item :label="t('使用base64')">
|
||||
<view class="flex flex-row">
|
||||
<cl-svg :src="base64" class="h-6 w-6"></cl-svg>
|
||||
</view>
|
||||
</demo-item>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { ref } from "vue";
|
||||
|
||||
const svg = ref(
|
||||
`<svg t="1756119341770" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7779" width="64" height="64"><path d="M783.1 899.3H242.9c-97.7 0-177.3-79.5-177.3-177.3V302.6c0-97.7 79.5-177.3 177.3-177.3H783c97.7 0 177.3 79.5 177.3 177.3V722c0.1 97.7-79.5 177.3-177.2 177.3zM242.9 214.8c-48.4 0-87.8 39.4-87.8 87.8V722c0 48.4 39.4 87.8 87.8 87.8H783c48.4 0 87.8-39.4 87.8-87.8V302.6c0-48.4-39.4-87.8-87.8-87.8H242.9z" fill="#333333" p-id="7780"></path><path d="M513 600.5c-24.9 0-49.9-7.3-71.6-21.8l-2.9-2.1-214.9-170.1c-19.4-15.3-22.7-43.5-7.3-62.8 15.3-19.4 43.5-22.6 62.8-7.3l213.2 168.8c12.7 7.8 28.7 7.8 41.5 0L747 336.4c19.3-15.3 47.5-12.1 62.8 7.3 15.3 19.4 12.1 47.5-7.3 62.8L584.6 578.7c-21.7 14.5-46.7 21.8-71.6 21.8z" fill="#333333" p-id="7781"></path></svg>`
|
||||
);
|
||||
|
||||
const base64 = ref(
|
||||
`data:image/svg+xml;base64,PHN2ZyB0PSIxNzU2MTE2OTQxMjk0IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9Ijc2MTYiIHdpZHRoPSI2NCIgaGVpZ2h0PSI2NCI+PHBhdGggZD0iTTU0MS44IDkyOC41aC02MS4xYy01MC42IDAtOTEuOC00MS4yLTkxLjgtOTEuOFY2MTMuNmMwLTUwLjYgNDEuMi05MS44IDkxLjgtOTEuOGg2MS4xYzUwLjYgMCA5MS44IDQxLjIgOTEuOCA5MS44djIyMy4xYzAgNTAuNy00MS4xIDkxLjgtOTEuOCA5MS44eiBtLTYxLjEtMzE2LjJsLTEuMyAyMjQuNCA2Mi41IDEuM2MwLjcgMCAxLjMtMC42IDEuMy0xLjNWNjEzLjZsLTYyLjUtMS4zek04MzQuOSA5MjguNWgtNjEuMWMtNTAuNiAwLTkxLjgtNDEuMi05MS44LTkxLjhWNDA5LjVjMC01MC42IDQxLjItOTEuOCA5MS44LTkxLjhoNjEuMWM1MC42IDAgOTEuOCA0MS4yIDkxLjggOTEuOHY0MjcuM2MwIDUwLjYtNDEuMiA5MS43LTkxLjggOTEuN3ogbS02MS4yLTUyMC40bC0xLjMgNDI4LjYgNjIuNSAxLjNjMC43IDAgMS4zLTAuNiAxLjMtMS4zVjQwOS41bC02Mi41LTEuNHpNMjUyLjQgOTI4LjVoLTYxLjFjLTUwLjYgMC05MS44LTQxLjItOTEuOC05MS44di04MC4yYzAtNTAuNiA0MS4yLTkxLjggOTEuOC05MS44aDYxLjFjNTAuNiAwIDkxLjggNDEuMiA5MS44IDkxLjh2ODAuMmMwIDUwLjctNDEuMiA5MS44LTkxLjggOTEuOHogbS02MS4xLTE3My4zbC0xLjMgODEuNSA2Mi41IDEuM2MwLjcgMCAxLjMtMC42IDEuMy0xLjN2LTgwLjJsLTYyLjUtMS4zek0xNDQuNiA2MjUuNWMtMTEuNiAwLTIzLjItNC40LTMyLTEzLjMtMTcuNy0xNy43LTE3LjYtNDYuMyAwLTY0TDUyNiAxMzUuM2MxNy43LTE3LjYgNDYuMy0xNy42IDY0IDAgMTcuNyAxNy43IDE3LjYgNDYuMyAwIDY0TDE3Ni42IDYxMi4yYy04LjkgOC44LTIwLjUgMTMuMy0zMiAxMy4zeiIgZmlsbD0iIzMzMzMzMyIgcC1pZD0iNzYxNyI+PC9wYXRoPjxwYXRoIGQ9Ik01ODguNCAzNjQuN2MtMjUgMC00NS4yLTIwLjMtNDUuMi00NS4yVjIxOC45YzAtMjAuMy0xNi41LTM2LjgtMzYuOC0zNi44SDQwNS44Yy0yNSAwLTQ1LjItMjAuMy00NS4yLTQ1LjJzMjAuMy00NS4yIDQ1LjItNDUuMmgxMDAuNmM3MC4yIDAgMTI3LjIgNTcuMSAxMjcuMiAxMjcuMnYxMDAuNmMwIDI1LTIwLjIgNDUuMi00NS4yIDQ1LjJ6IiBmaWxsPSIjMzMzMzMzIiBwLWlkPSI3NjE4Ij48L3BhdGg+PC9zdmc+`
|
||||
);
|
||||
</script>
|
||||
19
cool-unix/pages/demo/other/vibrate.uvue
Normal file
19
cool-unix/pages/demo/other/vibrate.uvue
Normal file
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-button @tap="onVibrate">{{ t("点击触发") }}</cl-button>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { vibrate } from "@/uni_modules/cool-vibrate";
|
||||
|
||||
function onVibrate() {
|
||||
vibrate(1);
|
||||
}
|
||||
</script>
|
||||
147
cool-unix/pages/demo/other/watermark.uvue
Normal file
147
cool-unix/pages/demo/other/watermark.uvue
Normal file
@@ -0,0 +1,147 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('自定义样式')">
|
||||
<view class="flex">
|
||||
<cl-watermark
|
||||
:text="customText"
|
||||
:font-size="fontSize"
|
||||
:color="color"
|
||||
:dark-color="darkColor"
|
||||
:opacity="opacity"
|
||||
:rotate="rotate"
|
||||
:width="width"
|
||||
:height="height"
|
||||
:gap-x="gapX"
|
||||
:gap-y="gapY"
|
||||
:font-weight="fontWeight"
|
||||
>
|
||||
<view
|
||||
class="flex flex-col p-4 rounded-xl bg-surface-50 dark:bg-surface-800 h-[400rpx]"
|
||||
>
|
||||
<cl-text>
|
||||
明月几时有?把酒问青天。 不知天上宫阙,今夕是何年。
|
||||
我欲乘风归去,又恐琼楼玉宇,高处不胜寒。 起舞弄清影,何似在人间。
|
||||
</cl-text>
|
||||
</view>
|
||||
</cl-watermark>
|
||||
</view>
|
||||
|
||||
<cl-list border class="mt-3">
|
||||
<cl-list-item :label="t('水印文本')">
|
||||
<cl-input
|
||||
v-model="customText"
|
||||
placeholder="请输入水印文本"
|
||||
:pt="{ className: 'w-[300rpx]' }"
|
||||
/>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('字体大小')">
|
||||
<view class="w-[300rpx]">
|
||||
<cl-slider v-model="fontSize" :min="12" :max="32" :step="1"></cl-slider>
|
||||
</view>
|
||||
</cl-list-item>
|
||||
|
||||
<!-- #ifndef APP -->
|
||||
<cl-list-item :label="t('透明度')">
|
||||
<view class="w-[300rpx]">
|
||||
<cl-slider
|
||||
v-model="opacity"
|
||||
:min="0.1"
|
||||
:max="1"
|
||||
:step="0.05"
|
||||
></cl-slider>
|
||||
</view>
|
||||
</cl-list-item>
|
||||
<!-- #endif -->
|
||||
|
||||
<cl-list-item :label="t('旋转角度')">
|
||||
<view class="w-[300rpx]">
|
||||
<cl-slider
|
||||
v-model="rotate"
|
||||
:min="-180"
|
||||
:max="180"
|
||||
:step="5"
|
||||
></cl-slider>
|
||||
</view>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('水印宽度')">
|
||||
<view class="w-[300rpx]">
|
||||
<cl-slider v-model="width" :min="80" :max="300" :step="10"></cl-slider>
|
||||
</view>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('水印高度')">
|
||||
<view class="w-[300rpx]">
|
||||
<cl-slider v-model="height" :min="40" :max="200" :step="10"></cl-slider>
|
||||
</view>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('水平间距')">
|
||||
<view class="w-[300rpx]">
|
||||
<cl-slider v-model="gapX" :min="20" :max="200" :step="10"></cl-slider>
|
||||
</view>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('垂直间距')">
|
||||
<view class="w-[300rpx]">
|
||||
<cl-slider v-model="gapY" :min="20" :max="200" :step="10"></cl-slider>
|
||||
</view>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('字体粗细')">
|
||||
<cl-tabs
|
||||
v-model="fontWeight"
|
||||
:list="fontWeightList"
|
||||
:height="60"
|
||||
show-slider
|
||||
></cl-tabs>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('图片保护')">
|
||||
<view class="flex">
|
||||
<cl-watermark text="© Cool UI" :width="200" :height="80" :opacity="0.9">
|
||||
<image
|
||||
src="https://unix.cool-js.com/images/demo/avatar.jpg"
|
||||
mode="aspectFit"
|
||||
class="w-full"
|
||||
></image>
|
||||
</cl-watermark>
|
||||
</view>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { ref } from "vue";
|
||||
import { t } from "@/locale";
|
||||
import type { ClTabsItem } from "@/uni_modules/cool-ui";
|
||||
|
||||
const customText = ref("Cool UI");
|
||||
const fontSize = ref(16);
|
||||
const color = ref("rgba(0, 0, 0, 0.15)");
|
||||
const darkColor = ref("rgba(255, 255, 255, 0.15)");
|
||||
const opacity = ref(1);
|
||||
const rotate = ref(-22);
|
||||
const width = ref(120);
|
||||
const height = ref(64);
|
||||
const gapX = ref(100);
|
||||
const gapY = ref(100);
|
||||
const fontWeight = ref("normal");
|
||||
|
||||
const fontWeightList = [
|
||||
{
|
||||
label: t("正常"),
|
||||
value: "normal"
|
||||
},
|
||||
{
|
||||
label: t("加粗"),
|
||||
value: "bold"
|
||||
}
|
||||
] as ClTabsItem[];
|
||||
</script>
|
||||
80
cool-unix/pages/demo/status/badge.uvue
Normal file
80
cool-unix/pages/demo/status/badge.uvue
Normal file
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<view class="flex flex-row overflow-visible">
|
||||
<cl-badge type="primary" value="1" class="mr-2"></cl-badge>
|
||||
<cl-badge type="success" value="12" class="mr-2"></cl-badge>
|
||||
<cl-badge type="warn" value="31" class="mr-2"></cl-badge>
|
||||
<cl-badge type="error" value="24" class="mr-2"></cl-badge>
|
||||
<cl-badge type="info" value="17" class="mr-2"></cl-badge>
|
||||
<cl-badge type="primary" value="41" class="mr-2"></cl-badge>
|
||||
<cl-badge type="success" value="56" class="mr-2"></cl-badge>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('结合按钮')">
|
||||
<view class="flex flex-row overflow-visible">
|
||||
<cl-button>
|
||||
{{ t("购买") }}
|
||||
<template #content>
|
||||
<cl-badge type="error" value="1" position> </cl-badge>
|
||||
</template>
|
||||
</cl-button>
|
||||
|
||||
<cl-button :pt="{ className: '!ml-5' }">
|
||||
{{ t("消息") }}
|
||||
<template #content>
|
||||
<cl-badge type="error" dot position> </cl-badge>
|
||||
</template>
|
||||
</cl-button>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('结合图片')">
|
||||
<view class="flex flex-row overflow-visible">
|
||||
<cl-image
|
||||
:pt="{
|
||||
className: 'overflow-visible'
|
||||
}"
|
||||
src="https://unix.cool-js.com/images/demo/bg1.png"
|
||||
>
|
||||
<cl-badge type="error" value="+9" position> </cl-badge>
|
||||
</cl-image>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('结合图标')">
|
||||
<view class="flex flex-row overflow-visible">
|
||||
<view class="relative overflow-visible">
|
||||
<cl-icon name="mail-line"> </cl-icon>
|
||||
<cl-badge
|
||||
type="error"
|
||||
dot
|
||||
position
|
||||
:pt="{
|
||||
className: '!top-[-6rpx] !right-[-6rpx]'
|
||||
}"
|
||||
>
|
||||
</cl-badge>
|
||||
</view>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义样式')">
|
||||
<view class="flex flex-row overflow-visible">
|
||||
<cl-badge
|
||||
type="info"
|
||||
:pt="{ className: '!rounded-bl-none' }"
|
||||
value="1"
|
||||
></cl-badge>
|
||||
</view>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
</script>
|
||||
84
cool-unix/pages/demo/status/countdown.uvue
Normal file
84
cool-unix/pages/demo/status/countdown.uvue
Normal file
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-countdown :datetime="datetime"></cl-countdown>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('隐藏为 00 的值')">
|
||||
<cl-countdown :minute="60" hide-zero></cl-countdown>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('指定天数')">
|
||||
<cl-countdown :day="2" format="{d}天{h}:{m}:{s}"></cl-countdown>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义模板')">
|
||||
<cl-countdown :day="1" format="{d}天{h}时{m}分{s}秒"></cl-countdown>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('指定小时')">
|
||||
<cl-countdown :hour="2"></cl-countdown>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('指定分钟')">
|
||||
<cl-countdown :minute="2"></cl-countdown>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('指定秒')">
|
||||
<cl-countdown :second="10"></cl-countdown>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('完成后提示')">
|
||||
<cl-countdown :second="5" @done="onDone"></cl-countdown>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('3秒后开始倒计时')">
|
||||
<cl-countdown ref="countdownRef" :second="5" :auto="false"></cl-countdown>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义样式')">
|
||||
<cl-countdown
|
||||
:hour="10"
|
||||
:pt="{
|
||||
text: {
|
||||
className: parseClass([
|
||||
'px-2 py-1 rounded-md',
|
||||
[isDark, 'bg-surface-700', 'bg-surface-100']
|
||||
])
|
||||
},
|
||||
splitor: {
|
||||
className: 'px-2'
|
||||
}
|
||||
}"
|
||||
></cl-countdown>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { dayUts, isDark, parseClass } from "@/cool";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
|
||||
const ui = useUi();
|
||||
|
||||
const datetime = ref(dayUts().add(1, "minute").toDate());
|
||||
|
||||
function onDone() {
|
||||
ui.showToast({
|
||||
message: "倒计时完成"
|
||||
});
|
||||
}
|
||||
|
||||
const countdownRef = ref<ClCountdownComponentPublicInstance | null>(null);
|
||||
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
countdownRef.value!.next();
|
||||
}, 3000);
|
||||
});
|
||||
</script>
|
||||
29
cool-unix/pages/demo/status/loadmore.uvue
Normal file
29
cool-unix/pages/demo/status/loadmore.uvue
Normal file
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-loadmore loading></cl-loadmore>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('3秒后加载完成')">
|
||||
<cl-loadmore :loading="loading" :finish="finish"></cl-loadmore>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { onMounted, ref } from "vue";
|
||||
|
||||
const loading = ref(true);
|
||||
const finish = ref(false);
|
||||
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
finish.value = true;
|
||||
}, 3000);
|
||||
});
|
||||
</script>
|
||||
46
cool-unix/pages/demo/status/noticebar.uvue
Normal file
46
cool-unix/pages/demo/status/noticebar.uvue
Normal file
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-noticebar
|
||||
text="云想衣裳花想容,春风拂槛露华浓。若非群玉山头见,会向瑶台月下逢。"
|
||||
></cl-noticebar>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('带图标')">
|
||||
<view class="flex flex-row items-center">
|
||||
<cl-icon name="notification-4-line" class="mr-2"></cl-icon>
|
||||
<cl-noticebar
|
||||
text="云想衣裳花想容,春风拂槛露华浓。若非群玉山头见,会向瑶台月下逢。"
|
||||
></cl-noticebar>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('设置速度')">
|
||||
<cl-noticebar
|
||||
:speed="40"
|
||||
text="云想衣裳花想容,春风拂槛露华浓。若非群玉山头见,会向瑶台月下逢。"
|
||||
></cl-noticebar>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('垂直方向')">
|
||||
<cl-noticebar
|
||||
direction="vertical"
|
||||
:text="[
|
||||
'江南可采莲,莲叶何田田',
|
||||
'鱼戏莲叶间',
|
||||
'鱼戏莲叶东',
|
||||
'鱼戏莲叶西',
|
||||
'鱼戏莲叶南',
|
||||
'鱼戏莲叶北'
|
||||
]"
|
||||
></cl-noticebar>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
</script>
|
||||
69
cool-unix/pages/demo/status/progress-circle.uvue
Normal file
69
cool-unix/pages/demo/status/progress-circle.uvue
Normal file
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('自定义')">
|
||||
<cl-progress-circle
|
||||
:value="value"
|
||||
:color="isColor ? 'red' : null"
|
||||
:un-color="isColor ? '#f7bfbf' : null"
|
||||
:size="isSize ? 80 : 120"
|
||||
:show-text="isText"
|
||||
:duration="isDuration ? 200 : 500"
|
||||
></cl-progress-circle>
|
||||
|
||||
<cl-list
|
||||
border
|
||||
:pt="{
|
||||
className: 'mt-5'
|
||||
}"
|
||||
>
|
||||
<cl-list-item :label="t('改个颜色')">
|
||||
<cl-switch v-model="isColor"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('显示文本')">
|
||||
<cl-switch v-model="isText"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('快一些')">
|
||||
<cl-switch v-model="isDuration"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('显示文本')">
|
||||
<cl-button type="light" size="small" icon="add-line" @tap="add"></cl-button>
|
||||
<cl-button
|
||||
type="light"
|
||||
size="small"
|
||||
icon="subtract-line"
|
||||
@tap="sub"
|
||||
></cl-button>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
|
||||
const isSize = ref(false);
|
||||
const isText = ref(true);
|
||||
const isColor = ref(false);
|
||||
const isDuration = ref(false);
|
||||
const value = ref(70);
|
||||
|
||||
function add() {
|
||||
if (value.value < 100) {
|
||||
value.value += 10;
|
||||
}
|
||||
}
|
||||
|
||||
function sub() {
|
||||
if (value.value > 0) {
|
||||
value.value -= 10;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
26
cool-unix/pages/demo/status/progress.uvue
Normal file
26
cool-unix/pages/demo/status/progress.uvue
Normal file
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-progress :value="50"></cl-progress>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义颜色')">
|
||||
<cl-progress :value="30" color="red" un-color="#f7bfbf"></cl-progress>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义宽度')">
|
||||
<cl-progress :value="30" :stroke-width="20"></cl-progress>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('不显示文本')">
|
||||
<cl-progress :value="75" :show-text="false"></cl-progress>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
</script>
|
||||
77
cool-unix/pages/demo/status/rolling-number.uvue
Normal file
77
cool-unix/pages/demo/status/rolling-number.uvue
Normal file
@@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('基础用法')">
|
||||
<cl-rolling-number :value="value"></cl-rolling-number>
|
||||
|
||||
<view class="flex flex-row mt-2">
|
||||
<cl-button icon="add-line" size="small" @tap="add"></cl-button>
|
||||
<cl-button
|
||||
icon="subtract-line"
|
||||
type="light"
|
||||
size="small"
|
||||
@tap="sub"
|
||||
></cl-button>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('自定义')">
|
||||
<view class="flex flex-col items-center">
|
||||
<cl-rolling-number
|
||||
:value="value"
|
||||
:pt="{
|
||||
className: parseClass([[isCustom, '!text-3xl']]),
|
||||
color: isCustom ? 'primary' : ''
|
||||
}"
|
||||
:duration="isSpeed ? 300 : 1000"
|
||||
:decimals="isDecimals ? 2 : 0"
|
||||
></cl-rolling-number>
|
||||
|
||||
<view class="flex flex-row mt-2">
|
||||
<cl-button icon="add-line" size="small" @tap="add"></cl-button>
|
||||
<cl-button
|
||||
icon="subtract-line"
|
||||
type="light"
|
||||
size="small"
|
||||
@tap="sub"
|
||||
></cl-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<cl-list border class="mt-3">
|
||||
<cl-list-item :label="t('加快滚动速度')">
|
||||
<cl-switch v-model="isSpeed"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('显示小数位数')">
|
||||
<cl-switch v-model="isDecimals"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('自定义样式')">
|
||||
<cl-switch v-model="isCustom"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { ref } from "vue";
|
||||
import { parseClass } from "@/cool";
|
||||
|
||||
const value = ref(100);
|
||||
const isSpeed = ref(false);
|
||||
const isDecimals = ref(false);
|
||||
const isCustom = ref(false);
|
||||
|
||||
function add() {
|
||||
value.value += 100;
|
||||
}
|
||||
|
||||
function sub() {
|
||||
value.value -= 100;
|
||||
}
|
||||
</script>
|
||||
61
cool-unix/pages/demo/status/skeleton.uvue
Normal file
61
cool-unix/pages/demo/status/skeleton.uvue
Normal file
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<demo-item :label="t('文本')">
|
||||
<cl-skeleton :loading="loading">
|
||||
<cl-text>云想衣裳花想容,春风拂槛露华浓。</cl-text>
|
||||
</cl-skeleton>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('按钮')">
|
||||
<view class="flex flex-row">
|
||||
<cl-skeleton type="button" :loading="loading">
|
||||
<cl-button>立即购买</cl-button>
|
||||
</cl-skeleton>
|
||||
</view>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('图片')">
|
||||
<cl-skeleton type="image" :loading="loading">
|
||||
<cl-image
|
||||
src="https://unix.cool-js.com/images/demo/bg1.png"
|
||||
></cl-image>
|
||||
</cl-skeleton>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('圆形')">
|
||||
<cl-skeleton type="circle" :loading="loading">
|
||||
<cl-image
|
||||
:radius="100"
|
||||
src="https://unix.cool-js.com/images/demo/bg1.png"
|
||||
></cl-image>
|
||||
</cl-skeleton>
|
||||
</demo-item>
|
||||
|
||||
<demo-item :label="t('组合')">
|
||||
<view class="flex flex-row">
|
||||
<cl-skeleton type="image" loading> </cl-skeleton>
|
||||
|
||||
<view class="flex-1 ml-2">
|
||||
<cl-skeleton type="text" loading> </cl-skeleton>
|
||||
<cl-skeleton type="text" loading class="mt-2 !w-[160rpx]"> </cl-skeleton>
|
||||
</view>
|
||||
</view>
|
||||
</demo-item>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from "@/locale";
|
||||
import DemoItem from "../components/item.uvue";
|
||||
import { ref } from "vue";
|
||||
|
||||
const loading = ref(true);
|
||||
|
||||
onReady(() => {
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
}, 3000);
|
||||
});
|
||||
</script>
|
||||
522
cool-unix/pages/index/home.uvue
Normal file
522
cool-unix/pages/index/home.uvue
Normal file
@@ -0,0 +1,522 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<cl-topbar fixed :show-back="false" safe-area-top :height="isMp() ? null : 100">
|
||||
<view
|
||||
class="flex flex-row items-center p-3 flex-1 w-full"
|
||||
:class="{
|
||||
'pt-0': isMp()
|
||||
}"
|
||||
>
|
||||
<view class="bg-primary-500 rounded-lg p-[12rpx]">
|
||||
<cl-image
|
||||
src="/static/logo.png"
|
||||
:width="32"
|
||||
:height="32"
|
||||
:show-loading="false"
|
||||
></cl-image>
|
||||
</view>
|
||||
|
||||
<cl-text
|
||||
color="primary"
|
||||
:pt="{
|
||||
className: '!text-xl mr-auto ml-2 flex-1'
|
||||
}"
|
||||
>
|
||||
{{ config.name }}
|
||||
</cl-text>
|
||||
|
||||
<view
|
||||
class="bg-surface-500 h-8 w-8 rounded-full flex flex-row items-center justify-center mr-3"
|
||||
@tap="setSize"
|
||||
>
|
||||
<cl-icon name="font-size" color="white"></cl-icon>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="bg-primary-500 h-8 w-8 rounded-full flex flex-row items-center justify-center"
|
||||
:class="{
|
||||
'mr-24': isMp()
|
||||
}"
|
||||
@tap="setLocale"
|
||||
>
|
||||
<cl-icon name="translate" color="white"></cl-icon>
|
||||
</view>
|
||||
</view>
|
||||
</cl-topbar>
|
||||
|
||||
<view class="p-3">
|
||||
<view class="group" v-for="item in data" :key="item.label">
|
||||
<cl-text :pt="{ className: '!text-sm !text-surface-400 mb-2 ml-2' }"
|
||||
>{{ item.label }}({{ item.children?.length ?? 0 }})</cl-text
|
||||
>
|
||||
|
||||
<view class="list">
|
||||
<cl-row :gutter="10">
|
||||
<template v-for="child in item.children" :key="child.label">
|
||||
<cl-col :span="6" v-if="child.hidden != true">
|
||||
<view
|
||||
class="item dark:!bg-surface-800"
|
||||
hover-class="opacity-80"
|
||||
:hover-stay-time="250"
|
||||
@tap="toPath(child)"
|
||||
>
|
||||
<cl-icon :name="child.icon" :size="36"></cl-icon>
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'mt-1 !text-xs text-center'
|
||||
}"
|
||||
>{{ child.label }}</cl-text
|
||||
>
|
||||
</view>
|
||||
</cl-col>
|
||||
</template>
|
||||
</cl-row>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 自定义底部导航栏 -->
|
||||
<custom-tabbar></custom-tabbar>
|
||||
|
||||
<!-- 主题设置 -->
|
||||
<locale-set :ref="refs.set('localeSet')"></locale-set>
|
||||
|
||||
<!-- 字体大小设置 -->
|
||||
<size-set :ref="refs.set('sizeSet')"></size-set>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { isApp, isMp, router, useRefs } from "@/cool";
|
||||
import { t } from "@/locale";
|
||||
import { computed } from "vue";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
import { config } from "@/config";
|
||||
import LocaleSet from "@/components/locale-set.uvue";
|
||||
import SizeSet from "@/components/size-set.uvue";
|
||||
import CustomTabbar from "@/components/tabbar.uvue";
|
||||
|
||||
const ui = useUi();
|
||||
const refs = useRefs();
|
||||
|
||||
type Item = {
|
||||
label: string;
|
||||
icon?: string;
|
||||
path?: string;
|
||||
disabled?: boolean;
|
||||
hidden?: boolean;
|
||||
children?: Item[];
|
||||
};
|
||||
|
||||
const data = computed<Item[]>(() => {
|
||||
return [
|
||||
{
|
||||
label: t("基础组件"),
|
||||
children: [
|
||||
{
|
||||
label: t("文本"),
|
||||
icon: "text",
|
||||
path: "/pages/demo/basic/text"
|
||||
},
|
||||
{
|
||||
label: t("按钮"),
|
||||
icon: "mouse-line",
|
||||
path: "/pages/demo/basic/button"
|
||||
},
|
||||
{
|
||||
label: t("图片"),
|
||||
icon: "image-line",
|
||||
path: "/pages/demo/basic/image"
|
||||
},
|
||||
{
|
||||
label: t("图标"),
|
||||
icon: "puzzle-2-line",
|
||||
path: "/pages/demo/basic/icon"
|
||||
},
|
||||
{
|
||||
label: t("标签"),
|
||||
icon: "price-tag-3-line",
|
||||
path: "/pages/demo/basic/tag"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: t("表单组件"),
|
||||
children: [
|
||||
{
|
||||
label: t("表单验证"),
|
||||
icon: "draft-line",
|
||||
path: "/pages/demo/form/form"
|
||||
},
|
||||
{
|
||||
label: t("输入框"),
|
||||
icon: "input-field",
|
||||
path: "/pages/demo/form/input"
|
||||
},
|
||||
{
|
||||
label: t("文本域"),
|
||||
icon: "text-block",
|
||||
path: "/pages/demo/form/textarea"
|
||||
},
|
||||
{
|
||||
label: t("计数器"),
|
||||
icon: "increase-decrease-line",
|
||||
path: "/pages/demo/form/input-number"
|
||||
},
|
||||
{
|
||||
label: t("口令输入"),
|
||||
icon: "input-method-line",
|
||||
path: "/pages/demo/form/input-otp"
|
||||
},
|
||||
{
|
||||
label: t("键盘"),
|
||||
icon: "keyboard-box-line",
|
||||
path: "/pages/demo/form/keyboard"
|
||||
},
|
||||
{
|
||||
label: t("单选框"),
|
||||
icon: "radio-button-line",
|
||||
path: "/pages/demo/form/radio"
|
||||
},
|
||||
{
|
||||
label: t("多选框"),
|
||||
icon: "checkbox-line",
|
||||
path: "/pages/demo/form/checkbox"
|
||||
},
|
||||
{
|
||||
label: t("开关"),
|
||||
icon: "toggle-line",
|
||||
path: "/pages/demo/form/switch"
|
||||
},
|
||||
{
|
||||
label: t("评分"),
|
||||
icon: "star-line",
|
||||
path: "/pages/demo/form/rate"
|
||||
},
|
||||
{
|
||||
label: t("滑块"),
|
||||
icon: "equalizer-2-line",
|
||||
path: "/pages/demo/form/slider"
|
||||
},
|
||||
{
|
||||
label: t("选择器"),
|
||||
icon: "dropdown-list",
|
||||
path: "/pages/demo/form/select"
|
||||
},
|
||||
{
|
||||
label: t("日期选择器"),
|
||||
icon: "calendar-line",
|
||||
path: "/pages/demo/form/select-date"
|
||||
},
|
||||
{
|
||||
label: t("时间选择器"),
|
||||
icon: "time-line",
|
||||
path: "/pages/demo/form/select-time"
|
||||
},
|
||||
{
|
||||
label: t("级联选择器"),
|
||||
icon: "stacked-view",
|
||||
path: "/pages/demo/form/cascader"
|
||||
},
|
||||
{
|
||||
label: t("文件上传"),
|
||||
icon: "file-upload-line",
|
||||
path: "/pages/demo/form/upload"
|
||||
},
|
||||
{
|
||||
label: t("日历"),
|
||||
icon: "calendar-line",
|
||||
path: "/pages/demo/form/calendar"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: t("布局组件"),
|
||||
children: [
|
||||
{
|
||||
label: t("弹性布局"),
|
||||
icon: "layout-2-line",
|
||||
path: "/pages/demo/layout/flex"
|
||||
},
|
||||
{
|
||||
label: t("标签页"),
|
||||
icon: "function-add-line",
|
||||
path: "/pages/demo/layout/tabs"
|
||||
},
|
||||
{
|
||||
label: t("折叠面板"),
|
||||
icon: "collapse-vertical-line",
|
||||
path: "/pages/demo/layout/collapse"
|
||||
},
|
||||
{
|
||||
label: t("吸顶"),
|
||||
icon: "align-top",
|
||||
path: "/pages/demo/layout/sticky"
|
||||
},
|
||||
{
|
||||
label: t("导航栏"),
|
||||
icon: "layout-top-line",
|
||||
path: "/pages/demo/layout/topbar"
|
||||
},
|
||||
{
|
||||
label: t("底部视图"),
|
||||
icon: "layout-bottom-line",
|
||||
path: "/pages/demo/layout/footer"
|
||||
},
|
||||
{
|
||||
label: t("悬浮视图"),
|
||||
icon: "picture-in-picture-line",
|
||||
path: "/pages/demo/layout/float-view"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: t("数据展示"),
|
||||
children: [
|
||||
{
|
||||
label: t("头像"),
|
||||
icon: "account-circle-line",
|
||||
path: "/pages/demo/data/avatar"
|
||||
},
|
||||
{
|
||||
label: t("查看更多"),
|
||||
icon: "menu-add-line",
|
||||
path: "/pages/demo/data/read-more"
|
||||
},
|
||||
{
|
||||
label: t("列表"),
|
||||
icon: "list-check",
|
||||
path: "/pages/demo/data/list"
|
||||
},
|
||||
{
|
||||
label: t("列表视图"),
|
||||
icon: "list-view",
|
||||
path: "/pages/demo/data/list-view"
|
||||
},
|
||||
{
|
||||
label: t("列表刷新"),
|
||||
icon: "refresh-line",
|
||||
path: "/pages/demo/data/list-view-refresh",
|
||||
disabled: false
|
||||
},
|
||||
{
|
||||
label: t("瀑布流"),
|
||||
icon: "layout-column-line",
|
||||
path: "/pages/demo/data/waterfall"
|
||||
},
|
||||
{
|
||||
label: t("轮播图"),
|
||||
icon: "carousel-view",
|
||||
path: "/pages/demo/data/banner"
|
||||
},
|
||||
{
|
||||
label: t("跑马灯"),
|
||||
icon: "film-line",
|
||||
path: "/pages/demo/data/marquee"
|
||||
},
|
||||
{
|
||||
label: t("分页"),
|
||||
icon: "page-separator",
|
||||
path: "/pages/demo/data/pagination"
|
||||
},
|
||||
{
|
||||
label: t("时间轴"),
|
||||
icon: "timeline-view",
|
||||
path: "/pages/demo/data/timeline"
|
||||
},
|
||||
{
|
||||
label: t("拖拽"),
|
||||
icon: "drag-move-line",
|
||||
path: "/pages/demo/data/draggable"
|
||||
},
|
||||
{
|
||||
label: t("筛选栏"),
|
||||
icon: "filter-line",
|
||||
path: "/pages/demo/data/filter-bar"
|
||||
},
|
||||
{
|
||||
label: t("树形结构"),
|
||||
icon: "node-tree",
|
||||
path: "/pages/demo/data/tree"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: t("状态组件"),
|
||||
children: [
|
||||
{
|
||||
label: t("角标"),
|
||||
icon: "notification-badge-line",
|
||||
path: "/pages/demo/status/badge"
|
||||
},
|
||||
{
|
||||
label: t("通知栏"),
|
||||
icon: "error-warning-line",
|
||||
path: "/pages/demo/status/noticebar"
|
||||
},
|
||||
{
|
||||
label: t("倒计时"),
|
||||
icon: "timer-line",
|
||||
path: "/pages/demo/status/countdown"
|
||||
},
|
||||
{
|
||||
label: t("数字滚动"),
|
||||
icon: "arrow-up-box-line",
|
||||
path: "/pages/demo/status/rolling-number"
|
||||
},
|
||||
{
|
||||
label: t("进度条"),
|
||||
icon: "subtract-line",
|
||||
path: "/pages/demo/status/progress"
|
||||
},
|
||||
{
|
||||
label: t("圆形进度条"),
|
||||
icon: "circle-line",
|
||||
path: "/pages/demo/status/progress-circle"
|
||||
},
|
||||
{
|
||||
label: t("骨架图"),
|
||||
icon: "shadow-line",
|
||||
path: "/pages/demo/status/skeleton"
|
||||
},
|
||||
{
|
||||
label: t("加载更多"),
|
||||
icon: "loader-4-line",
|
||||
path: "/pages/demo/status/loadmore"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: t("反馈组件"),
|
||||
children: [
|
||||
{
|
||||
label: t("操作菜单"),
|
||||
icon: "menu-line",
|
||||
path: "/pages/demo/feedback/action-sheet"
|
||||
},
|
||||
{
|
||||
label: t("弹窗"),
|
||||
icon: "chat-4-line",
|
||||
path: "/pages/demo/feedback/popup"
|
||||
},
|
||||
{
|
||||
label: t("确认框"),
|
||||
icon: "chat-check-line",
|
||||
path: "/pages/demo/feedback/confirm"
|
||||
},
|
||||
{
|
||||
label: t("提示框"),
|
||||
icon: "message-2-line",
|
||||
path: "/pages/demo/feedback/toast"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "其他",
|
||||
children: [
|
||||
{
|
||||
label: "QRCode",
|
||||
icon: "qr-code-line",
|
||||
path: "/pages/demo/other/qrcode"
|
||||
},
|
||||
{
|
||||
label: t("签名"),
|
||||
icon: "sketching",
|
||||
path: "/pages/demo/other/sign"
|
||||
},
|
||||
{
|
||||
label: t("水印"),
|
||||
icon: "copyright-line",
|
||||
path: "/pages/demo/other/watermark"
|
||||
},
|
||||
{
|
||||
label: t("图片裁剪"),
|
||||
icon: "crop-line",
|
||||
path: "/pages/demo/other/cropper"
|
||||
},
|
||||
{
|
||||
label: t("Canvas"),
|
||||
icon: "markup-line",
|
||||
path: "/pages/demo/other/canvas"
|
||||
},
|
||||
{
|
||||
label: t("富文本"),
|
||||
icon: "text-snippet",
|
||||
path: "/pages/demo/other/rict-text",
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
label: "DayUts",
|
||||
icon: "timer-2-line",
|
||||
path: "/pages/demo/other/day-uts"
|
||||
},
|
||||
{
|
||||
label: "Vibrate",
|
||||
icon: "volume-vibrate-line",
|
||||
path: "/pages/demo/other/vibrate"
|
||||
},
|
||||
{
|
||||
label: "SVG",
|
||||
icon: "bubble-chart-line",
|
||||
path: "/pages/demo/other/svg"
|
||||
},
|
||||
{
|
||||
label: "SlideVerify",
|
||||
icon: "contract-right-fill",
|
||||
path: "/pages/demo/other/slide-verify"
|
||||
},
|
||||
{
|
||||
label: "Animation",
|
||||
icon: "instance-line",
|
||||
path: "/pages/demo/other/animation"
|
||||
},
|
||||
{
|
||||
label: "Router",
|
||||
icon: "compass-discover-line",
|
||||
path: "/pages/demo/other/router/index"
|
||||
},
|
||||
{
|
||||
label: "Share",
|
||||
icon: "share-line",
|
||||
path: "/pages/demo/other/share",
|
||||
hidden: !isApp()
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
function toPath(item: Item) {
|
||||
if (item.disabled == true) {
|
||||
return ui.showToast({
|
||||
message: t("该功能正在开发中")
|
||||
});
|
||||
}
|
||||
|
||||
router.to(item.path!);
|
||||
}
|
||||
|
||||
function setLocale() {
|
||||
refs.open("localeSet");
|
||||
}
|
||||
|
||||
function setSize() {
|
||||
refs.open("sizeSet");
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.group {
|
||||
@apply mb-10;
|
||||
|
||||
.list {
|
||||
.item {
|
||||
@apply flex flex-col items-center;
|
||||
@apply rounded-xl bg-white px-2;
|
||||
height: 140rpx;
|
||||
margin-bottom: 10rpx;
|
||||
padding-top: 36rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
324
cool-unix/pages/index/my.uvue
Normal file
324
cool-unix/pages/index/my.uvue
Normal file
@@ -0,0 +1,324 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<cl-topbar
|
||||
fixed
|
||||
:height="100"
|
||||
:show-back="false"
|
||||
safe-area-top
|
||||
background-color="transparent"
|
||||
>
|
||||
<view class="flex flex-row items-center w-full flex-1 px-3">
|
||||
<view class="top-icon dark:!bg-surface-700" @tap="toSet">
|
||||
<cl-icon name="settings-line"></cl-icon>
|
||||
</view>
|
||||
|
||||
<view class="top-icon dark:!bg-surface-700" @tap="toTest">
|
||||
<cl-icon name="notification-4-line"></cl-icon>
|
||||
</view>
|
||||
</view>
|
||||
</cl-topbar>
|
||||
|
||||
<view class="p-3">
|
||||
<view class="flex flex-col justify-center items-center pt-6 pb-3">
|
||||
<view class="relative overflow-visible" @tap="toEdit">
|
||||
<cl-avatar
|
||||
:src="userInfo?.avatarUrl"
|
||||
:size="150"
|
||||
:pt="{ className: '!rounded-3xl', icon: { size: 60 } }"
|
||||
>
|
||||
</cl-avatar>
|
||||
|
||||
<view
|
||||
class="flex flex-col justify-center items-center absolute bottom-0 right-[-6rpx] bg-black rounded-full p-1"
|
||||
v-if="!user.isNull()"
|
||||
>
|
||||
<cl-icon name="edit-line" color="white" :size="24"></cl-icon>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="flex-1 flex flex-col justify-center items-center w-full" @tap="toEdit">
|
||||
<cl-text :pt="{ className: '!text-xl mt-5 mb-1 font-bold' }">{{
|
||||
userInfo?.nickName ?? t("未登录")
|
||||
}}</cl-text>
|
||||
<cl-text color="info" v-if="!user.isNull()">{{ userInfo?.phone }}</cl-text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<cl-row
|
||||
:pt="{
|
||||
className: 'pt-3 pb-6'
|
||||
}"
|
||||
>
|
||||
<cl-col :span="6">
|
||||
<view class="flex flex-col items-center justify-center">
|
||||
<cl-rolling-number
|
||||
:pt="{ className: '!text-xl' }"
|
||||
:value="171"
|
||||
></cl-rolling-number>
|
||||
<cl-text :pt="{ className: 'mt-1 !text-xs' }" color="info">{{
|
||||
t("总点击")
|
||||
}}</cl-text>
|
||||
</view>
|
||||
</cl-col>
|
||||
|
||||
<cl-col :span="6">
|
||||
<view class="flex flex-col items-center justify-center">
|
||||
<cl-rolling-number
|
||||
:pt="{ className: '!text-xl' }"
|
||||
:value="24"
|
||||
></cl-rolling-number>
|
||||
<cl-text :pt="{ className: 'mt-1 !text-xs' }" color="info">{{
|
||||
t("赞")
|
||||
}}</cl-text>
|
||||
</view>
|
||||
</cl-col>
|
||||
|
||||
<cl-col :span="6">
|
||||
<view class="flex flex-col items-center justify-center">
|
||||
<cl-rolling-number
|
||||
:pt="{ className: '!text-xl' }"
|
||||
:value="89"
|
||||
></cl-rolling-number>
|
||||
<cl-text :pt="{ className: 'mt-1 !text-xs' }" color="info">{{
|
||||
t("收藏")
|
||||
}}</cl-text>
|
||||
</view>
|
||||
</cl-col>
|
||||
|
||||
<cl-col :span="6">
|
||||
<view class="flex flex-col items-center justify-center">
|
||||
<cl-rolling-number
|
||||
:pt="{ className: '!text-xl' }"
|
||||
:value="653"
|
||||
></cl-rolling-number>
|
||||
<cl-text :pt="{ className: 'mt-1 !text-xs' }" color="info">{{
|
||||
t("粉丝")
|
||||
}}</cl-text>
|
||||
</view>
|
||||
</cl-col>
|
||||
</cl-row>
|
||||
|
||||
<cl-row :gutter="20" :pt="{ className: 'mb-3' }">
|
||||
<cl-col :span="12">
|
||||
<view class="bg-white dark:!bg-surface-800 p-4 rounded-2xl flex flex-row">
|
||||
<view class="flex flex-col mr-auto">
|
||||
<cl-text
|
||||
ellipsis
|
||||
:pt="{
|
||||
className: '!w-[180rpx]'
|
||||
}"
|
||||
>{{ t("接单模式") }}</cl-text
|
||||
>
|
||||
<cl-text :pt="{ className: '!text-xs mt-1' }" color="info">{{
|
||||
t("已关闭")
|
||||
}}</cl-text>
|
||||
</view>
|
||||
|
||||
<cl-switch></cl-switch>
|
||||
</view>
|
||||
</cl-col>
|
||||
|
||||
<cl-col :span="12">
|
||||
<view class="bg-white dark:!bg-surface-800 p-4 rounded-2xl flex flex-row">
|
||||
<view class="flex flex-col mr-auto">
|
||||
<cl-text
|
||||
ellipsis
|
||||
:pt="{
|
||||
className: '!w-[180rpx]'
|
||||
}"
|
||||
>{{ t("消息通知") }}</cl-text
|
||||
>
|
||||
<cl-text :pt="{ className: '!text-xs mt-1' }" color="info">{{
|
||||
t("已关闭")
|
||||
}}</cl-text>
|
||||
</view>
|
||||
|
||||
<cl-switch></cl-switch>
|
||||
</view>
|
||||
</cl-col>
|
||||
</cl-row>
|
||||
|
||||
<view class="bg-white dark:!bg-surface-800 py-5 rounded-2xl mb-3 h-[160rpx]">
|
||||
<cl-row :pt="{ className: 'overflow-visible' }">
|
||||
<cl-col :span="6">
|
||||
<view class="flex flex-col justify-center items-center px-2">
|
||||
<cl-icon name="money-cny-circle-line" :size="46"></cl-icon>
|
||||
<cl-text
|
||||
:pt="{ className: '!text-xs mt-2 text-center' }"
|
||||
color="info"
|
||||
>{{ t("待支付") }}</cl-text
|
||||
>
|
||||
</view>
|
||||
</cl-col>
|
||||
|
||||
<cl-col :span="6">
|
||||
<view class="flex flex-col justify-center items-center px-2">
|
||||
<cl-icon name="box-1-line" :size="46"></cl-icon>
|
||||
<cl-text
|
||||
:pt="{ className: '!text-xs mt-2 text-center' }"
|
||||
color="info"
|
||||
>{{ t("未发货") }}</cl-text
|
||||
>
|
||||
</view>
|
||||
</cl-col>
|
||||
|
||||
<cl-col :span="6">
|
||||
<view
|
||||
class="flex flex-col justify-center items-center relative overflow-visible px-2"
|
||||
>
|
||||
<cl-icon name="flight-takeoff-line" :size="46"></cl-icon>
|
||||
<cl-text
|
||||
:pt="{ className: '!text-xs mt-2 text-center' }"
|
||||
color="info"
|
||||
>{{ t("已发货") }}</cl-text
|
||||
>
|
||||
|
||||
<cl-badge
|
||||
type="primary"
|
||||
:value="3"
|
||||
position
|
||||
:pt="{ className: '!right-6' }"
|
||||
></cl-badge>
|
||||
</view>
|
||||
</cl-col>
|
||||
|
||||
<cl-col :span="6">
|
||||
<view class="flex flex-col justify-center items-center px-2">
|
||||
<cl-icon name="exchange-cny-line" :size="46"></cl-icon>
|
||||
<cl-text
|
||||
:pt="{ className: '!text-xs mt-2 text-center' }"
|
||||
color="info"
|
||||
>{{ t("售后 / 退款") }}</cl-text
|
||||
>
|
||||
</view>
|
||||
</cl-col>
|
||||
</cl-row>
|
||||
</view>
|
||||
|
||||
<cl-list :pt="{ className: 'mb-3' }">
|
||||
<cl-list-item
|
||||
:label="t('我的钱包')"
|
||||
icon="wallet-line"
|
||||
arrow
|
||||
hoverable
|
||||
@tap="toTest"
|
||||
>
|
||||
</cl-list-item>
|
||||
<cl-list-item
|
||||
:label="t('我的日报')"
|
||||
icon="file-text-line"
|
||||
arrow
|
||||
hoverable
|
||||
@tap="toDailyReport"
|
||||
>
|
||||
</cl-list-item>
|
||||
<cl-list-item
|
||||
:label="t('我的周报')"
|
||||
icon="file-list-line"
|
||||
arrow
|
||||
hoverable
|
||||
@tap="toWeeklyReport"
|
||||
>
|
||||
</cl-list-item>
|
||||
<cl-list-item
|
||||
:label="t('工作建议')"
|
||||
icon="chat-voice-ai-line"
|
||||
arrow
|
||||
hoverable
|
||||
@tap="toAdvice"
|
||||
>
|
||||
</cl-list-item>
|
||||
<cl-list-item
|
||||
:label="t('我的月报')"
|
||||
icon="calendar-todo-line"
|
||||
arrow
|
||||
hoverable
|
||||
@tap="toMonthlyReport"
|
||||
>
|
||||
</cl-list-item>
|
||||
<cl-list-item
|
||||
:label="t('数据看板')"
|
||||
icon="pie-chart-line"
|
||||
arrow
|
||||
hoverable
|
||||
@tap="toTest"
|
||||
>
|
||||
</cl-list-item>
|
||||
<cl-list-item
|
||||
:label="t('历史记录')"
|
||||
icon="history-line"
|
||||
arrow
|
||||
hoverable
|
||||
@tap="toTest"
|
||||
>
|
||||
</cl-list-item>
|
||||
<cl-list-item
|
||||
:label="t('邀请好友')"
|
||||
icon="share-line"
|
||||
arrow
|
||||
hoverable
|
||||
@tap="toTest"
|
||||
>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
|
||||
<cl-list>
|
||||
<cl-list-item :label="t('设置')" icon="settings-line" arrow hoverable @tap="toSet">
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</view>
|
||||
|
||||
<!-- 自定义底部导航栏 -->
|
||||
<custom-tabbar></custom-tabbar>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { router, userInfo, useStore } from "@/cool";
|
||||
import { t } from "@/locale";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
import CustomTabbar from "@/components/tabbar.uvue";
|
||||
|
||||
const { user } = useStore();
|
||||
const ui = useUi();
|
||||
|
||||
function toTest() {
|
||||
ui.showToast({
|
||||
message: t("开发中,敬请期待")
|
||||
});
|
||||
}
|
||||
|
||||
function toDailyReport() {
|
||||
router.to("/pages/dailyreport/list");
|
||||
}
|
||||
|
||||
function toWeeklyReport() {
|
||||
router.to("/pages/weeklyreport/list");
|
||||
}
|
||||
|
||||
function toAdvice() {
|
||||
router.to('/pages/advice/index');
|
||||
}
|
||||
|
||||
function toMonthlyReport() {
|
||||
router.to("/pages/monthlyreport/list");
|
||||
}
|
||||
|
||||
function toSet() {
|
||||
router.to("/pages/set/index");
|
||||
}
|
||||
|
||||
function toEdit() {
|
||||
router.to("/pages/user/edit");
|
||||
}
|
||||
|
||||
onReady(() => {
|
||||
user.get();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.top-icon {
|
||||
@apply flex items-center justify-center rounded-lg bg-white mr-3 p-2;
|
||||
}
|
||||
</style>
|
||||
121
cool-unix/pages/index/template.uvue
Normal file
121
cool-unix/pages/index/template.uvue
Normal file
@@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<cl-list
|
||||
v-for="(item, index) in list"
|
||||
:key="index"
|
||||
:title="item.label"
|
||||
:pt="{
|
||||
className: 'mb-5'
|
||||
}"
|
||||
>
|
||||
<cl-list-item
|
||||
v-for="child in item.children"
|
||||
:key="child.label"
|
||||
:label="child.label"
|
||||
:arrow="child.path != null"
|
||||
@tap="toPath(child)"
|
||||
>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</view>
|
||||
|
||||
<!-- 自定义底部导航栏 -->
|
||||
<custom-tabbar></custom-tabbar>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import CustomTabbar from "@/components/tabbar.uvue";
|
||||
import { router } from "@/cool";
|
||||
import { t } from "@/locale";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
import { computed } from "vue";
|
||||
|
||||
const ui = useUi();
|
||||
|
||||
type Item = {
|
||||
label: string;
|
||||
path?: string;
|
||||
children?: Item[];
|
||||
};
|
||||
|
||||
const list = computed<Item[]>(() => [
|
||||
{
|
||||
label: t("社交"),
|
||||
children: [
|
||||
{
|
||||
label: t("帖子详情"),
|
||||
path: "/pages/template/post/detail"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: t("商城"),
|
||||
children: [
|
||||
{
|
||||
label: t("商品分类"),
|
||||
path: "/pages/template/shop/goods-category"
|
||||
},
|
||||
{
|
||||
label: t("商品详情"),
|
||||
path: "/pages/template/shop/goods-detail/index"
|
||||
},
|
||||
{
|
||||
label: t("商品列表、筛选")
|
||||
},
|
||||
{
|
||||
label: t("购物车"),
|
||||
path: "/pages/template/shop/shopping-cart"
|
||||
},
|
||||
{
|
||||
label: t("订单列表、详情")
|
||||
},
|
||||
{
|
||||
label: t("收货地址"),
|
||||
path: "/pages/template/shop/address"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: t("聊天"),
|
||||
children: [
|
||||
{
|
||||
label: t("对话列表、历史记录")
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "AI",
|
||||
children: [
|
||||
{
|
||||
label: t("流式回复")
|
||||
},
|
||||
{
|
||||
label: t("语言合成")
|
||||
},
|
||||
{
|
||||
label: t("语音识别")
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: t("其他"),
|
||||
children: [
|
||||
{
|
||||
label: t("文件管理")
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
function toPath(item: Item) {
|
||||
if (item.path == null) {
|
||||
return ui.showToast({
|
||||
message: t("该模板正在开发中")
|
||||
});
|
||||
}
|
||||
|
||||
router.to(item.path!);
|
||||
}
|
||||
</script>
|
||||
93
cool-unix/pages/monthlyreport/detail.uvue
Normal file
93
cool-unix/pages/monthlyreport/detail.uvue
Normal file
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<cl-page :title="'\u6708\u62A5\u8BE6\u60C5'">
|
||||
<view v-if="loading" class="flex justify-center items-center py-20">
|
||||
<cl-loading />
|
||||
</view>
|
||||
|
||||
<view v-else-if="report" class="p-4">
|
||||
<!-- header -->
|
||||
<view class="mb-6">
|
||||
<view class="flex justify-between items-center mb-2">
|
||||
<text class="text-2xl font-bold">{{ report.month || '-' }}</text>
|
||||
</view>
|
||||
<text class="text-sm text-gray-500">{{ report.submitTime ? ('\u63D0\u4EA4\u4E8E ' + formatDateTime(report.submitTime)) : (report.createTime ? ('\u521B\u5EFA\u4E8E ' + formatDateTime(report.createTime)) : '') }}</text>
|
||||
</view>
|
||||
|
||||
<!-- content blocks -->
|
||||
<view class="mb-6">
|
||||
<text class="text-base font-bold mb-3 block">{{ '\u6708\u62A5\u5185\u5BB9' }}</text>
|
||||
<view class="p-4 bg-white rounded-lg shadow-sm">
|
||||
<text class="text-sm text-gray-800">{{ report.userEditedContent || report.aiFormattedContent || report.originalText || '\u6682\u65E0\u5185\u5BB9' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="report.originalText" class="mb-4">
|
||||
<text class="text-base font-bold mb-2">{{ '\u539F\u59CB\u8F93\u5165\u5185\u5BB9' }}</text>
|
||||
<view class="p-3 bg-gray-50 rounded">
|
||||
<text class="text-sm text-gray-700">{{ report.originalText }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="report.aiFormattedContent" class="mb-4">
|
||||
<text class="text-base font-bold mb-2">{{ 'AI\u751F\u6210\u5185\u5BB9' }}</text>
|
||||
<view class="p-3 bg-blue-50 rounded">
|
||||
<text class="text-sm text-gray-800">{{ report.aiFormattedContent }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-else class="flex flex-col items-center justify-center py-20">
|
||||
<text class="text-6xl mb-4">{{ '\uD83D\uDCC5' }}</text>
|
||||
<text class="text-gray-400 text-base">{{ '\u6708\u62A5\u4E0D\u5B58\u5728' }}</text>
|
||||
<cl-button type="primary" size="small" class="mt-4" @tap="router.back()">{{ '\u8FD4\u56DE\u5217\u8868' }}</cl-button>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { request, router } from '@/cool';
|
||||
import { useUi } from '@/uni_modules/cool-ui';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
|
||||
const ui = useUi();
|
||||
const report = ref<any>(null);
|
||||
const loading = ref(true);
|
||||
const reportId = ref('');
|
||||
|
||||
onLoad(async (options: any) => {
|
||||
reportId.value = (options?.id ?? '').toString();
|
||||
if (!reportId.value) {
|
||||
ui.showToast({ message: '\u53C2\u6570\u9519\u8BEF\uFF1A\u7F3A\u5C11id', type: 'error' });
|
||||
setTimeout(() => router.back(), 700);
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
await loadDetail();
|
||||
});
|
||||
|
||||
async function loadDetail() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await request({ url: '/app/monthlyreport/report/detail', method: 'GET', params: { id: reportId.value } });
|
||||
report.value = res || null;
|
||||
} catch (e: any) {
|
||||
ui.showToast({ message: '\u52A0\u8F7D\u5931\u8D25: ' + (e.message || '\u672A\u77E5\u9519\u8BEF'), type: 'error' });
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function formatDateTime(v: string) {
|
||||
if (!v) return '';
|
||||
const d = new Date(v);
|
||||
const Y = d.getFullYear();
|
||||
const M = String(d.getMonth() + 1).padStart(2, '0');
|
||||
const D = String(d.getDate()).padStart(2, '0');
|
||||
const h = String(d.getHours()).padStart(2, '0');
|
||||
const m = String(d.getMinutes()).padStart(2, '0');
|
||||
return `${Y}-${M}-${D} ${h}:${m}`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
152
cool-unix/pages/monthlyreport/list.uvue
Normal file
152
cool-unix/pages/monthlyreport/list.uvue
Normal file
@@ -0,0 +1,152 @@
|
||||
<template>
|
||||
<cl-page title="我的月报">
|
||||
<view class="p-4">
|
||||
<!-- 筛选 -->
|
||||
<view class="flex items-center gap-3 mb-4">
|
||||
<cl-segmented
|
||||
v-model="filterType"
|
||||
:list="[
|
||||
{ label: '全部', value: 'all' },
|
||||
{ label: '草稿', value: 'draft' },
|
||||
{ label: '已提交', value: 'submitted' }
|
||||
]"
|
||||
/>
|
||||
<cl-button size="small" @tap="refresh">刷新</cl-button>
|
||||
</view>
|
||||
|
||||
<!-- 列表 -->
|
||||
<scroll-view scroll-y style="height: calc(100vh - 160px)" @scrolltolower="loadMore">
|
||||
<view v-if="list.length > 0">
|
||||
<view v-for="item in list" :key="item.id" class="p-4 mb-3 bg-white rounded-2xl" @tap="toDetail(item)">
|
||||
<view class="flex items-center justify-between mb-1">
|
||||
<text class="text-base font-bold">{{ item.month }}</text>
|
||||
<view
|
||||
:class="[
|
||||
'px-2 py-1 rounded text-xs',
|
||||
item.status === 1 ? 'bg-green-100 text-green-700' : 'bg-yellow-100 text-yellow-700'
|
||||
]"
|
||||
>
|
||||
{{ item.status === 1 ? "已提交" : "草稿" }}
|
||||
</view>
|
||||
</view>
|
||||
<text class="text-gray-500 text-sm">{{ brief(item.userEditedContent || item.aiFormattedContent || item.originalText) }}</text>
|
||||
<view class="text-xs text-gray-400 mt-2">
|
||||
<text v-if="item.submitTime">提交于 {{ formatTime(item.submitTime) }}</text>
|
||||
<text v-else-if="item.createTime">创建于 {{ formatTime(item.createTime) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="loading" class="flex items-center justify-center py-3">
|
||||
<text class="text-gray-400">加载中...</text>
|
||||
</view>
|
||||
<view v-else-if="finished" class="flex items-center justify-center py-3">
|
||||
<text class="text-gray-400">没有更多了</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="flex flex-col items-center justify-center py-20">
|
||||
<text class="text-6xl mb-3">📅</text>
|
||||
<text class="text-gray-400">暂无月报</text>
|
||||
<view class="mt-4 px-4 py-2 bg-blue-500 text-white rounded" @tap="toSubmit">
|
||||
<text>去提交月报</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 悬浮提交按钮 -->
|
||||
<view class="fixed bottom-20 right-4">
|
||||
<cl-button type="primary" size="large" round @tap="toSubmit">
|
||||
<text class="text-2xl">✏️</text>
|
||||
</cl-button>
|
||||
</view>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { request, router, useStore } from '@/cool';
|
||||
import { useUi } from '@/uni_modules/cool-ui';
|
||||
|
||||
const ui = useUi();
|
||||
const { user } = useStore();
|
||||
|
||||
const list = ref<any[]>([]);
|
||||
const loading = ref(false);
|
||||
const finished = ref(false);
|
||||
const page = ref(1);
|
||||
const pageSize = ref(20);
|
||||
const filterType = ref<'all' | 'draft' | 'submitted'>('all');
|
||||
const userId = ref(0);
|
||||
|
||||
onMounted(async () => {
|
||||
if (user.token) {
|
||||
try { await user.get(); } catch {}
|
||||
}
|
||||
if (user.info.value?.id) {
|
||||
userId.value = user.info.value.id;
|
||||
loadReports();
|
||||
} else {
|
||||
ui.showToast({ message: '请先登录', type: 'error' });
|
||||
setTimeout(() => router.to('/pages/user/login'), 1000);
|
||||
}
|
||||
});
|
||||
|
||||
function brief(s: string = '') {
|
||||
const t = s.replace(/\n/g, ' ');
|
||||
return t.length > 60 ? t.slice(0, 60) + '…' : t;
|
||||
}
|
||||
|
||||
function statusClass(status: number) {
|
||||
return status === 1 ? 'text-green-600' : 'text-orange-500';
|
||||
}
|
||||
|
||||
function statusText(status: number) {
|
||||
return status === 1 ? '已提交' : '草稿';
|
||||
}
|
||||
|
||||
function formatTime(v: string) {
|
||||
try {
|
||||
const d = new Date(v);
|
||||
const Y = d.getFullYear();
|
||||
const M = String(d.getMonth() + 1).padStart(2, '0');
|
||||
const D = String(d.getDate()).padStart(2, '0');
|
||||
const h = String(d.getHours()).padStart(2, '0');
|
||||
const m = String(d.getMinutes()).padStart(2, '0');
|
||||
const s = String(d.getSeconds()).padStart(2, '0');
|
||||
return `${Y}-${M}-${D} ${h}:${m}:${s}`;
|
||||
} catch (e) { return v; }
|
||||
}
|
||||
|
||||
async function loadReports() {
|
||||
if (loading.value || finished.value) return;
|
||||
loading.value = true;
|
||||
try {
|
||||
const params: any = { userId: userId.value, page: page.value, size: pageSize.value };
|
||||
if (filterType.value === 'draft') params.status = 0;
|
||||
if (filterType.value === 'submitted') params.status = 1;
|
||||
const res = await request({ url: '/app/monthlyreport/report/myReports', method: 'GET', params });
|
||||
if (res && res.length > 0) {
|
||||
if (page.value === 1) list.value = res; else list.value.push(...res);
|
||||
if (res.length < pageSize.value) finished.value = true;
|
||||
page.value++;
|
||||
} else {
|
||||
if (page.value === 1) list.value = [];
|
||||
finished.value = true;
|
||||
}
|
||||
} catch (e: any) {
|
||||
ui.showToast({ message: e.message || '加载失败', type: 'error' });
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function loadMore() { loadReports(); }
|
||||
|
||||
function refresh() {
|
||||
page.value = 1; finished.value = false; list.value = []; loadReports();
|
||||
}
|
||||
|
||||
function toSubmit() { router.to('/pages/monthlyreport/submit'); }
|
||||
function toDetail(item: any) { router.to(`/pages/monthlyreport/detail?id=${item.id}`); }
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
173
cool-unix/pages/monthlyreport/submit.uvue
Normal file
173
cool-unix/pages/monthlyreport/submit.uvue
Normal file
@@ -0,0 +1,173 @@
|
||||
<template>
|
||||
<cl-page title="提交月报">
|
||||
<view class="p-4">
|
||||
<!-- 月份选择 -->
|
||||
<view class="mb-4">
|
||||
<text class="text-base font-bold mb-2">月份</text>
|
||||
<cl-input v-model="form.month" placeholder="例如:2025-11" />
|
||||
</view>
|
||||
|
||||
<!-- 原始内容输入 -->
|
||||
<view class="mb-4">
|
||||
<text class="text-base font-bold mb-2">工作内容(可选)</text>
|
||||
<cl-input v-model="originalText" placeholder="请输入本月工作要点(可选)" :maxlength="4000" />
|
||||
</view>
|
||||
|
||||
<!-- 从周报生成 -->
|
||||
<view class="mb-4">
|
||||
<cl-button type="primary" size="large" block @tap="generateFromWeekly" :loading="isGenerating">
|
||||
{{ isGenerating ? '生成中...' : '🧩 从我的周报生成' }}
|
||||
</cl-button>
|
||||
</view>
|
||||
|
||||
<!-- AI格式化 -->
|
||||
<view v-if="originalText" class="mb-4">
|
||||
<cl-button type="success" size="large" block @tap="formatWithAI" :loading="isFormatting">
|
||||
{{ isFormatting ? 'AI生成中...' : '🤖 AI格式化' }}
|
||||
</cl-button>
|
||||
</view>
|
||||
|
||||
<!-- AI结果与最终编辑 -->
|
||||
<view v-if="aiFormattedContent" class="mb-4">
|
||||
<text class="text-base font-bold mb-2">AI生成的月报</text>
|
||||
<view class="p-3 bg-green-50 rounded-lg mb-2"><cl-markdown :content="aiFormattedContent" /></view>
|
||||
<cl-input v-model="userEditedContent" placeholder="请编辑最终月报内容" :maxlength="6000" />
|
||||
</view>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="flex gap-3">
|
||||
<cl-button type="default" class="flex-1" @tap="saveDraft" :loading="saving">保存草稿</cl-button>
|
||||
<cl-button type="primary" class="flex-1" @tap="submitReport" :loading="submitting">提交</cl-button>
|
||||
</view>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { request, useStore, router } from '@/cool';
|
||||
import { useUi } from '@/uni_modules/cool-ui';
|
||||
|
||||
const ui = useUi();
|
||||
const { user } = useStore();
|
||||
|
||||
const form = ref<any>({ month: '' });
|
||||
const originalText = ref('');
|
||||
const aiFormattedContent = ref('');
|
||||
const userEditedContent = ref('');
|
||||
const isFormatting = ref(false);
|
||||
const isGenerating = ref(false);
|
||||
const saving = ref(false);
|
||||
const submitting = ref(false);
|
||||
|
||||
onMounted(async () => {
|
||||
if (user.token) {
|
||||
try { await user.get(); } catch {}
|
||||
}
|
||||
});
|
||||
|
||||
function ensureMonth(): string | null {
|
||||
const v = (form.value.month || '').trim();
|
||||
if (!/^\d{4}-\d{2}$/.test(v)) {
|
||||
ui.showToast({ message: '请输入有效月份,如 2025-11', type: 'error' });
|
||||
return null;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
async function generateFromWeekly() {
|
||||
const month = ensureMonth();
|
||||
if (!month) return;
|
||||
if (!user.info.value?.id) {
|
||||
ui.showToast({ message: '请先登录', type: 'error' });
|
||||
router.to('/pages/user/login');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
isGenerating.value = true;
|
||||
const res = await request({
|
||||
url: '/app/monthlyreport/report/generateFromWeekly',
|
||||
method: 'POST',
|
||||
data: {
|
||||
userId: user.info.value.id,
|
||||
month,
|
||||
templateKey: 'monthly_report_format'
|
||||
}
|
||||
});
|
||||
aiFormattedContent.value = res.formattedContent || '';
|
||||
userEditedContent.value = aiFormattedContent.value;
|
||||
} catch (e: any) {
|
||||
ui.showToast({ message: e.message || '生成失败', type: 'error' });
|
||||
} finally {
|
||||
isGenerating.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function formatWithAI() {
|
||||
const month = ensureMonth();
|
||||
if (!month) return;
|
||||
try {
|
||||
isFormatting.value = true;
|
||||
const res = await request({
|
||||
url: '/app/monthlyreport/report/aiFormat',
|
||||
method: 'POST',
|
||||
data: { originalText: originalText.value, templateKey: 'monthly_report_format', month }
|
||||
});
|
||||
aiFormattedContent.value = res.formattedContent || '';
|
||||
userEditedContent.value = aiFormattedContent.value;
|
||||
} catch (e: any) {
|
||||
ui.showToast({ message: e.message || 'AI格式化失败', type: 'error' });
|
||||
} finally {
|
||||
isFormatting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function saveDraft() {
|
||||
const month = ensureMonth();
|
||||
if (!month || !user.info.value?.id) return;
|
||||
try {
|
||||
saving.value = true;
|
||||
await request({
|
||||
url: '/app/monthlyreport/report/saveDraft',
|
||||
method: 'POST',
|
||||
data: {
|
||||
userId: user.info.value.id,
|
||||
month,
|
||||
originalText: originalText.value,
|
||||
aiFormattedContent: aiFormattedContent.value,
|
||||
userEditedContent: userEditedContent.value,
|
||||
inputType: 0
|
||||
}
|
||||
});
|
||||
ui.showToast({ message: '已保存草稿', type: 'success' });
|
||||
} catch (e: any) {
|
||||
ui.showToast({ message: e.message || '保存失败', type: 'error' });
|
||||
} finally { saving.value = false; }
|
||||
}
|
||||
|
||||
async function submitReport() {
|
||||
const month = ensureMonth();
|
||||
if (!month || !user.info.value?.id) return;
|
||||
try {
|
||||
submitting.value = true;
|
||||
await request({
|
||||
url: '/app/monthlyreport/report/submit',
|
||||
method: 'POST',
|
||||
data: {
|
||||
userId: user.info.value.id,
|
||||
month,
|
||||
originalText: originalText.value,
|
||||
aiFormattedContent: aiFormattedContent.value,
|
||||
userEditedContent: userEditedContent.value,
|
||||
inputType: 0
|
||||
}
|
||||
});
|
||||
ui.showToast({ message: '提交成功', type: 'success' });
|
||||
router.back();
|
||||
} catch (e: any) {
|
||||
ui.showToast({ message: e.message || '提交失败', type: 'error' });
|
||||
} finally { submitting.value = false; }
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
44
cool-unix/pages/set/about.uvue
Normal file
44
cool-unix/pages/set/about.uvue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<view class="flex flex-col items-center justify-center py-10">
|
||||
<view class="p-3 bg-primary-500 rounded-xl">
|
||||
<cl-image src="/static/logo.png" :height="80" :width="80"></cl-image>
|
||||
</view>
|
||||
<cl-text :pt="{ className: 'mt-3 mb-1' }">{{ config.name }}</cl-text>
|
||||
<cl-text
|
||||
color="info"
|
||||
:pt="{
|
||||
className: '!text-xs'
|
||||
}"
|
||||
>version {{ config.version }}</cl-text
|
||||
>
|
||||
</view>
|
||||
|
||||
<cl-list>
|
||||
<cl-list-item
|
||||
:label="t('访问官网')"
|
||||
arrow
|
||||
hoverable
|
||||
@tap="toWebSite"
|
||||
></cl-list-item>
|
||||
</cl-list>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { config } from "@/config";
|
||||
import { $t, t } from "@/locale";
|
||||
import { openWeb } from "@/uni_modules/cool-open-web";
|
||||
|
||||
onReady(() => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: $t("关于{name}", { name: config.name })
|
||||
});
|
||||
});
|
||||
|
||||
function toWebSite() {
|
||||
openWeb(config.website);
|
||||
}
|
||||
</script>
|
||||
39
cool-unix/pages/set/cs.uvue
Normal file
39
cool-unix/pages/set/cs.uvue
Normal file
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-10 flex flex-col items-center justify-center">
|
||||
<cl-text :pt="{ className: 'text-center mb-5' }"
|
||||
>我们期待与您携手,共同探索技术的边界,共同推动技术的进步</cl-text
|
||||
>
|
||||
|
||||
<view class="p-2 bg-white mb-5 rounded-xl">
|
||||
<cl-image
|
||||
src="/static/cs.png"
|
||||
:height="320"
|
||||
:width="320"
|
||||
show-menu-by-longpress
|
||||
></cl-image>
|
||||
</view>
|
||||
|
||||
<!-- #ifndef H5 -->
|
||||
<cl-button type="light" icon="download-line" @tap="saveImage">保存图片</cl-button>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
|
||||
const ui = useUi();
|
||||
|
||||
function saveImage() {
|
||||
uni.saveImageToPhotosAlbum({
|
||||
filePath: "/static/cs.png",
|
||||
success: () => {
|
||||
ui.showToast({
|
||||
message: "保存成功"
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
42
cool-unix/pages/set/general.uvue
Normal file
42
cool-unix/pages/set/general.uvue
Normal file
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<cl-list>
|
||||
<cl-list-item :label="t('深色模式')">
|
||||
<cl-switch :model-value="isDark" @change="onThemeChange"></cl-switch>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('多语言')" arrow hoverable @tap="setLocale"> </cl-list-item>
|
||||
|
||||
<cl-list-item :label="t('字体大小')" arrow hoverable @tap="setSize"> </cl-list-item>
|
||||
</cl-list>
|
||||
</view>
|
||||
|
||||
<!-- 语言设置 -->
|
||||
<locale-set :ref="refs.set('localeSet')"></locale-set>
|
||||
|
||||
<!-- 字体大小设置 -->
|
||||
<size-set :ref="refs.set('sizeSet')"></size-set>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { isDark, toggleTheme, useRefs } from "@/cool";
|
||||
import { t } from "@/locale";
|
||||
import LocaleSet from "@/components/locale-set.uvue";
|
||||
import SizeSet from "@/components/size-set.uvue";
|
||||
|
||||
const refs = useRefs();
|
||||
|
||||
function onThemeChange() {
|
||||
toggleTheme();
|
||||
}
|
||||
|
||||
function setLocale() {
|
||||
refs.open("localeSet");
|
||||
}
|
||||
|
||||
function setSize() {
|
||||
refs.open("sizeSet");
|
||||
}
|
||||
</script>
|
||||
79
cool-unix/pages/set/index.uvue
Normal file
79
cool-unix/pages/set/index.uvue
Normal file
@@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<cl-list :pt="{ className: 'mb-3' }">
|
||||
<cl-list-item
|
||||
:label="t('通用设置')"
|
||||
icon="settings-line"
|
||||
arrow
|
||||
hoverable
|
||||
@tap="router.to('/pages/set/general')"
|
||||
>
|
||||
</cl-list-item>
|
||||
<cl-list-item
|
||||
:label="t('通知设置')"
|
||||
icon="notification-4-line"
|
||||
arrow
|
||||
hoverable
|
||||
@tap="router.to('/pages/set/notice')"
|
||||
>
|
||||
</cl-list-item>
|
||||
<cl-list-item :label="t('隐私设置')" icon="lock-line" arrow hoverable>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
|
||||
<cl-list :pt="{ className: 'mb-3' }">
|
||||
<cl-list-item
|
||||
:label="$t('关于{name}', { name: config.name })"
|
||||
icon="error-warning-line"
|
||||
arrow
|
||||
hoverable
|
||||
:pt="{
|
||||
label: {
|
||||
className: 'flex-1'
|
||||
}
|
||||
}"
|
||||
@tap="router.to('/pages/set/about')"
|
||||
>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item
|
||||
:label="t('联系客服')"
|
||||
icon="customer-service-line"
|
||||
arrow
|
||||
hoverable
|
||||
@tap="router.to('/pages/set/cs')"
|
||||
>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
|
||||
<cl-list :pt="{ className: 'mb-3' }">
|
||||
<cl-list-item hoverable justify="center" @tap="toLogout">
|
||||
<cl-text color="error">{{ t("退出登录") }}</cl-text>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { config } from "@/config";
|
||||
import { router, useStore } from "@/cool";
|
||||
import { $t, t } from "@/locale";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
|
||||
const ui = useUi();
|
||||
const { user } = useStore();
|
||||
|
||||
function toLogout() {
|
||||
ui.showConfirm({
|
||||
title: t("提示"),
|
||||
message: t("确定退出登录吗?"),
|
||||
callback(action) {
|
||||
if (action == "confirm") {
|
||||
user.logout();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
15
cool-unix/pages/set/notice.uvue
Normal file
15
cool-unix/pages/set/notice.uvue
Normal file
@@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<cl-list>
|
||||
<cl-list-item :label="t('开启通知')">
|
||||
<cl-switch></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { t } from "@/locale";
|
||||
</script>
|
||||
399
cool-unix/pages/template/post/detail.uvue
Normal file
399
cool-unix/pages/template/post/detail.uvue
Normal file
@@ -0,0 +1,399 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<cl-sticky>
|
||||
<cl-topbar fixed safe-area-top :height="isMp() ? null : 100">
|
||||
<view class="flex flex-row items-center w-full pl-[72rpx] pr-3">
|
||||
<cl-avatar
|
||||
rounded
|
||||
:size="60"
|
||||
src="https://unix.cool-js.com/images/demo/avatar.jpg"
|
||||
></cl-avatar>
|
||||
<cl-text :pt="{ className: 'mx-3' }">神仙都没用</cl-text>
|
||||
|
||||
<cl-button
|
||||
text
|
||||
border
|
||||
size="small"
|
||||
rounded
|
||||
:pt="{ className: parseClass(['!px-3', [!isMp(), 'ml-auto']]) }"
|
||||
>立即关注</cl-button
|
||||
>
|
||||
</view>
|
||||
</cl-topbar>
|
||||
</cl-sticky>
|
||||
|
||||
<view class="banner">
|
||||
<cl-banner
|
||||
height="1000rpx"
|
||||
:list="bannerList"
|
||||
:pt="{
|
||||
className: '!rounded-none',
|
||||
image: {
|
||||
className: '!rounded-none'
|
||||
}
|
||||
}"
|
||||
></cl-banner>
|
||||
</view>
|
||||
|
||||
<view class="bg-white dark:bg-surface-800 mb-3">
|
||||
<view
|
||||
class="p-4 border border-solid border-b-surface-200 dark:border-b-surface-600 border-t-0 border-l-0 border-r-0"
|
||||
>
|
||||
<cl-text bold :pt="{ className: 'mb-2 text-lg' }">❄️ 雪地旅行商务团价格表</cl-text>
|
||||
|
||||
<cl-text :pt="{ className: 'mb-3' }">
|
||||
对于不想做攻略的小伙伴来说定制团真的是最佳选择。雪地自驾没有经验的真心不建议。费用清晰明了。预算很准确,收费很合理。</cl-text
|
||||
>
|
||||
|
||||
<view class="flex-row mb-4">
|
||||
<view
|
||||
class="flex-row items-center border border-solid border-surface-300 dark:border-surface-600 rounded-lg px-3 py-1 pl-2"
|
||||
>
|
||||
<cl-icon name="map-pin-2-line" :size="30"></cl-icon>
|
||||
<cl-text :pt="{ className: 'text-sm ml-1' }"
|
||||
>哈尔滨 · 圣 · 索菲亚教堂</cl-text
|
||||
>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<cl-text :pt="{ className: 'text-sm' }" color="info">编辑于 2天前</cl-text>
|
||||
</view>
|
||||
|
||||
<!-- 评论 -->
|
||||
<view class="p-4">
|
||||
<cl-text :pt="{ className: 'mb-4 text-sm' }">共 10 条评论</cl-text>
|
||||
|
||||
<!-- 评论输入框 -->
|
||||
<view class="flex-row reply items-center mb-5" @tap="openReply()">
|
||||
<cl-avatar
|
||||
:size="68"
|
||||
rounded
|
||||
:pt="{ className: 'mr-2' }"
|
||||
src="https://unix.cool-js.com/images/demo/avatar.jpg"
|
||||
></cl-avatar>
|
||||
|
||||
<view
|
||||
class="h-[69rpx] flex-1 flex-row items-center bg-surface-100 dark:bg-surface-700 rounded-full px-4"
|
||||
>
|
||||
<cl-text color="info" :pt="{ className: 'text-sm flex-1' }"
|
||||
>说点什么...</cl-text
|
||||
>
|
||||
|
||||
<cl-icon name="user-smile-line" :size="40"></cl-icon>
|
||||
|
||||
<cl-icon
|
||||
name="image-circle-line"
|
||||
:pt="{ className: 'ml-2' }"
|
||||
:size="40"
|
||||
></cl-icon>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="list">
|
||||
<view class="flex-row mb-6" v-for="item in commentList" :key="item.id">
|
||||
<cl-avatar :size="68" rounded :src="item.avatar"></cl-avatar>
|
||||
|
||||
<view class="ml-3 flex-1">
|
||||
<!-- 评论者信息 -->
|
||||
<view class="flex-row items-center mb-1">
|
||||
<cl-text :pt="{ className: 'text-sm' }" color="info">{{
|
||||
item.name
|
||||
}}</cl-text>
|
||||
|
||||
<cl-tag
|
||||
plain
|
||||
rounded
|
||||
:pt="{
|
||||
className: '!px-1 !py-[2rpx] ml-2',
|
||||
text: { className: '!text-xs' }
|
||||
}"
|
||||
v-if="item.isAuthor"
|
||||
>作者</cl-tag
|
||||
>
|
||||
</view>
|
||||
|
||||
<!-- 评论内容 -->
|
||||
<cl-text :pt="{ className: 'mb-1' }">{{ item.content }}</cl-text>
|
||||
|
||||
<view class="flex-row items-center">
|
||||
<!-- 评论时间 -->
|
||||
<cl-text :pt="{ className: 'text-sm' }" color="info">
|
||||
{{ item.time }}
|
||||
</cl-text>
|
||||
|
||||
<cl-text
|
||||
color="info"
|
||||
:pt="{ className: 'text-sm ml-3' }"
|
||||
@tap="openReply()"
|
||||
>回复</cl-text
|
||||
>
|
||||
</view>
|
||||
|
||||
<!-- 回复列表 -->
|
||||
<view class="flex-row mt-3" v-for="reply in item.reply" :key="reply.id">
|
||||
<cl-avatar :size="50" rounded :src="reply.avatar"> </cl-avatar>
|
||||
|
||||
<view class="ml-3 flex-1">
|
||||
<view class="flex-row items-center mb-1">
|
||||
<cl-text :pt="{ className: 'text-sm' }" color="info">{{
|
||||
reply.name
|
||||
}}</cl-text>
|
||||
|
||||
<cl-tag
|
||||
plain
|
||||
rounded
|
||||
:pt="{
|
||||
className: '!px-1 !py-[2rpx] ml-2',
|
||||
text: { className: '!text-xs' }
|
||||
}"
|
||||
v-if="reply.isAuthor"
|
||||
>作者</cl-tag
|
||||
>
|
||||
</view>
|
||||
|
||||
<cl-text :pt="{ className: 'mb-1' }">{{
|
||||
reply.content
|
||||
}}</cl-text>
|
||||
|
||||
<view class="flex-row items-center mb-1">
|
||||
<cl-text :pt="{ className: 'text-sm' }" color="info">
|
||||
{{ reply.time }}
|
||||
</cl-text>
|
||||
|
||||
<cl-text
|
||||
color="info"
|
||||
:pt="{ className: 'text-sm ml-3' }"
|
||||
@tap="openReply()"
|
||||
>回复</cl-text
|
||||
>
|
||||
</view>
|
||||
|
||||
<cl-text color="info" :pt="{ className: 'text-sm' }"
|
||||
>展开 1 条回复</cl-text
|
||||
>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<cl-footer>
|
||||
<view class="flex flex-row h-[68rpx]">
|
||||
<view
|
||||
class="flex-row items-center bg-surface-100 dark:bg-surface-700 rounded-full px-3 py-2 mr-6 w-[260rpx] h-full"
|
||||
@tap="openReply"
|
||||
>
|
||||
<cl-icon name="edit-line" color="info" :size="32"></cl-icon>
|
||||
<cl-text color="info" :pt="{ className: 'text-sm ml-2' }">说点什么...</cl-text>
|
||||
</view>
|
||||
|
||||
<view class="flex flex-row flex-1 justify-end h-full items-center">
|
||||
<view class="flex-row justify-center items-center mr-6">
|
||||
<cl-icon name="heart-line" :size="40"></cl-icon>
|
||||
<cl-text :pt="{ className: 'ml-2 text-sm' }">700</cl-text>
|
||||
</view>
|
||||
|
||||
<view class="flex-row justify-center items-center">
|
||||
<cl-icon name="chat-3-line" :size="40"></cl-icon>
|
||||
<cl-text :pt="{ className: 'ml-2 text-sm' }">59</cl-text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</cl-footer>
|
||||
|
||||
<!-- 回复弹窗 -->
|
||||
<cl-popup v-model="replyVisible" direction="bottom" ref="replyPopupRef">
|
||||
<view class="p-4 pt-0">
|
||||
<view class="bg-surface-100 dark:bg-surface-800 rounded-2xl p-[8rpx] h-[168rpx]">
|
||||
<cl-textarea
|
||||
placeholder="说点什么..."
|
||||
:border="false"
|
||||
:pt="{
|
||||
className: parseClass(['!bg-transparent'])
|
||||
}"
|
||||
autofocus
|
||||
v-if="isApp() ? true : replyPopupRef?.isOpened == true"
|
||||
></cl-textarea>
|
||||
</view>
|
||||
|
||||
<view class="flex-row items-center mt-3 pl-2">
|
||||
<cl-icon
|
||||
name="image-circle-line"
|
||||
:pt="{ className: 'mr-4' }"
|
||||
:size="44"
|
||||
></cl-icon>
|
||||
<cl-icon
|
||||
name="user-smile-line"
|
||||
:pt="{ className: 'mr-4' }"
|
||||
:size="44"
|
||||
></cl-icon>
|
||||
|
||||
<cl-button :pt="{ className: 'ml-auto !px-4' }" rounded>发送</cl-button>
|
||||
</view>
|
||||
</view>
|
||||
</cl-popup>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { isApp, isMp, parseClass } from "@/cool";
|
||||
import { ref } from "vue";
|
||||
|
||||
const bannerList = ref<string[]>([
|
||||
"https://unix.cool-js.com/images/demo/bg1.png",
|
||||
"https://unix.cool-js.com/images/demo/bg2.png",
|
||||
"https://unix.cool-js.com/images/demo/bg3.png"
|
||||
]);
|
||||
|
||||
type Comment = {
|
||||
id: number;
|
||||
avatar: string;
|
||||
name: string;
|
||||
content: string;
|
||||
time: string;
|
||||
isAuthor: boolean;
|
||||
order: number;
|
||||
reply: Comment[];
|
||||
};
|
||||
|
||||
const commentList = ref<Comment[]>([
|
||||
{
|
||||
id: 1,
|
||||
avatar: "https://unix.cool-js.com/images/demo/avatar-1.jpg",
|
||||
name: "李明",
|
||||
content: "导游讲解很专业,风景绝美,是一次难忘的旅行体验!",
|
||||
time: "2024-06-20 10:15",
|
||||
isAuthor: false,
|
||||
order: 1,
|
||||
reply: []
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
avatar: "https://unix.cool-js.com/images/demo/avatar-2.jpg",
|
||||
name: "小芳",
|
||||
content: "酒店干净卫生,位置也很方便,强烈推荐给大家!",
|
||||
time: "2024-06-20 11:08",
|
||||
isAuthor: false,
|
||||
order: 2,
|
||||
reply: [
|
||||
{
|
||||
id: 201,
|
||||
avatar: "https://unix.cool-js.com/images/demo/avatar.jpg",
|
||||
name: "神仙都没用",
|
||||
content: "感谢您的推荐,欢迎再次光临哦~",
|
||||
time: "2024-06-20 12:00",
|
||||
isAuthor: true,
|
||||
order: 1,
|
||||
reply: []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
avatar: "https://unix.cool-js.com/images/demo/avatar-3.jpg",
|
||||
name: "王科",
|
||||
content: "行程安排合理,吃住都很满意,导游态度超级好。",
|
||||
time: "2024-06-19 09:22",
|
||||
isAuthor: false,
|
||||
order: 3,
|
||||
reply: []
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
avatar: "https://unix.cool-js.com/images/demo/avatar-4.jpg",
|
||||
name: "艳艳",
|
||||
content: "第一次带父母出游,家人都开心,下次还会选择这里~",
|
||||
time: "2024-06-18 15:40",
|
||||
isAuthor: false,
|
||||
order: 4,
|
||||
reply: []
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
avatar: "https://unix.cool-js.com/images/demo/avatar-5.jpg",
|
||||
name: "孙强",
|
||||
content: "车程舒适,时间安排紧凑但不赶,非常不错,非常棒!",
|
||||
time: "2024-06-17 17:05",
|
||||
isAuthor: false,
|
||||
order: 5,
|
||||
reply: [
|
||||
{
|
||||
id: 501,
|
||||
avatar: "https://unix.cool-js.com/images/demo/avatar.jpg",
|
||||
name: "神仙都没用",
|
||||
content: "您的好评就是我们最大的动力,谢谢支持~",
|
||||
time: "2024-06-17 17:45",
|
||||
isAuthor: true,
|
||||
order: 1,
|
||||
reply: []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
avatar: "https://unix.cool-js.com/images/demo/avatar-6.jpg",
|
||||
name: "导游小陈",
|
||||
content: "很高兴大家玩的开心,有问题随时联系我们。",
|
||||
time: "2024-06-17 18:20",
|
||||
isAuthor: false,
|
||||
order: 6,
|
||||
reply: []
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
avatar: "https://unix.cool-js.com/images/demo/avatar-7.jpg",
|
||||
name: "Grace",
|
||||
content: "风景如画,照片拍了一堆,朋友都夸赞说好美!",
|
||||
time: "2024-06-16 14:10",
|
||||
isAuthor: false,
|
||||
order: 7,
|
||||
reply: []
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
avatar: "https://unix.cool-js.com/images/demo/avatar-8.jpg",
|
||||
name: "阿伟",
|
||||
content: "活动内容丰富,小朋友特别喜欢,下次会再来~",
|
||||
time: "2024-06-15 19:28",
|
||||
isAuthor: false,
|
||||
order: 8,
|
||||
reply: []
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
avatar: "https://unix.cool-js.com/images/demo/avatar-9.jpg",
|
||||
name: "小虎",
|
||||
content: "天气晴朗,安排贴心,吃到了很多特色美食,值了!",
|
||||
time: "2024-06-14 13:52",
|
||||
isAuthor: false,
|
||||
order: 9,
|
||||
reply: []
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
avatar: "https://unix.cool-js.com/images/demo/avatar-10.jpg",
|
||||
name: "Julia",
|
||||
content: "贴心的讲解和服务,体验很棒,感谢本次旅行团队!",
|
||||
time: "2024-06-13 16:34",
|
||||
isAuthor: false,
|
||||
order: 10,
|
||||
reply: []
|
||||
}
|
||||
]);
|
||||
|
||||
const replyPopupRef = ref<ClPopupComponentPublicInstance | null>(null);
|
||||
|
||||
const replyVisible = ref(false);
|
||||
|
||||
function openReply() {
|
||||
replyVisible.value = true;
|
||||
}
|
||||
|
||||
function closeReply() {
|
||||
replyVisible.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
199
cool-unix/pages/template/shop/address-edit.uvue
Normal file
199
cool-unix/pages/template/shop/address-edit.uvue
Normal file
@@ -0,0 +1,199 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<view class="p-4 bg-white rounded-2xl dark:!bg-surface-800 mb-3">
|
||||
<cl-form ref="formRef" v-model="formData" :rules="rules" :disabled="saving">
|
||||
<cl-form-item :label="t('收货人')" prop="contact" required>
|
||||
<cl-input
|
||||
v-model="formData.contact"
|
||||
:placeholder="t('请输入收货人姓名')"
|
||||
></cl-input>
|
||||
</cl-form-item>
|
||||
|
||||
<cl-form-item :label="t('手机号')" prop="phone" required>
|
||||
<cl-input
|
||||
v-model="formData.phone"
|
||||
:placeholder="t('请输入手机号')"
|
||||
:maxlength="11"
|
||||
type="number"
|
||||
></cl-input>
|
||||
</cl-form-item>
|
||||
|
||||
<cl-form-item :label="t('地区')" prop="province" required>
|
||||
<cl-cascader
|
||||
v-model="regions"
|
||||
:placeholder="t('选择省市区')"
|
||||
:options="pcaOptions"
|
||||
@change="onRegionsChange"
|
||||
></cl-cascader>
|
||||
</cl-form-item>
|
||||
|
||||
<cl-form-item :label="t('详细地址')" prop="address" required>
|
||||
<cl-input
|
||||
v-model="formData.address"
|
||||
:placeholder="t('小区楼栋、门牌号、村等')"
|
||||
></cl-input>
|
||||
</cl-form-item>
|
||||
</cl-form>
|
||||
</view>
|
||||
|
||||
<cl-list>
|
||||
<cl-list-item :label="t('默认地址')">
|
||||
<cl-switch v-model="formData.isDefault"></cl-switch>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
</view>
|
||||
|
||||
<cl-footer>
|
||||
<cl-button @tap="save()">{{ t("保存") }}</cl-button>
|
||||
</cl-footer>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { router, isEmpty, type Response, request, parse } from "@/cool";
|
||||
import { t } from "@/locale";
|
||||
import { useCascader, useForm, useUi, type ClFormRule } from "@/uni_modules/cool-ui";
|
||||
import { type Ref, ref } from "vue";
|
||||
import pca from "@/data/pca.json";
|
||||
import type { UserAddress } from "../types";
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
});
|
||||
|
||||
const ui = useUi();
|
||||
|
||||
const { formRef, validate } = useForm();
|
||||
|
||||
// 省市区级联选项数据
|
||||
const pcaOptions = useCascader(pca);
|
||||
|
||||
// 地区选择的值,格式为 [省, 市, 区]
|
||||
const regions = ref<string[]>([]);
|
||||
|
||||
// 表单数据,包含收货人、手机号、地区、详细地址、是否默认等字段
|
||||
const formData = ref<UserAddress>({
|
||||
contact: "",
|
||||
phone: "",
|
||||
province: "",
|
||||
city: "",
|
||||
district: "",
|
||||
address: "",
|
||||
isDefault: false
|
||||
}) as Ref<UserAddress>;
|
||||
|
||||
// 表单验证规则,校验收货人、手机号、详细地址、地区等必填项
|
||||
const rules = new Map<string, ClFormRule[]>([
|
||||
["contact", [{ required: true, message: t("收货人不能为空") }]],
|
||||
[
|
||||
"phone",
|
||||
[
|
||||
{ required: true, message: t("手机号不能为空") },
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: t("手机号格式不正确") }
|
||||
]
|
||||
],
|
||||
["address", [{ required: true, message: t("详细地址不能为空") }]],
|
||||
["province", [{ required: true, message: t("所在地区不能为空") }]]
|
||||
]);
|
||||
|
||||
// 保存按钮loading状态
|
||||
const saving = ref(false);
|
||||
|
||||
/**
|
||||
* 保存地址信息
|
||||
* 1. 校验表单
|
||||
* 2. 组装数据
|
||||
* 3. 请求后端接口,新增或更新地址
|
||||
*/
|
||||
function save() {
|
||||
validate((valid, errors) => {
|
||||
if (valid) {
|
||||
ui.showLoading(t("保存中"));
|
||||
|
||||
// 解构地区信息
|
||||
const [province, city, district] = regions.value;
|
||||
|
||||
saving.value = true;
|
||||
|
||||
// 合并表单数据和地区信息
|
||||
const data = {
|
||||
...formData.value,
|
||||
province,
|
||||
city,
|
||||
district
|
||||
};
|
||||
|
||||
// 根据是否有id判断是新增还是编辑
|
||||
request({
|
||||
url: `/app/user/address/${props.id != "" ? "update" : "add"}`,
|
||||
method: "POST",
|
||||
data
|
||||
})
|
||||
.then(() => {
|
||||
// 保存成功返回上一页
|
||||
router.back();
|
||||
})
|
||||
.catch((err) => {
|
||||
// 保存失败提示错误信息
|
||||
ui.showToast({ message: (err as Response).message! });
|
||||
})
|
||||
.finally(() => {
|
||||
ui.hideLoading();
|
||||
saving.value = false;
|
||||
});
|
||||
} else {
|
||||
// 校验失败提示第一个错误
|
||||
ui.showToast({ message: errors[0].message });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取地址详情(编辑时调用)
|
||||
* 1. 请求后端接口获取地址详情
|
||||
* 2. 回填表单和地区选择
|
||||
*/
|
||||
function getInfo() {
|
||||
request({
|
||||
url: "/app/user/address/info",
|
||||
data: { id: props.id }
|
||||
})
|
||||
.then((res) => {
|
||||
if (res != null) {
|
||||
// 解析并赋值表单数据
|
||||
formData.value = parse<UserAddress>(res)!;
|
||||
// 回填地区选择
|
||||
regions.value = [
|
||||
formData.value.province,
|
||||
formData.value.city,
|
||||
formData.value.district
|
||||
];
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
ui.showToast({ message: (err as Response).message! });
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 地区选择变化时触发
|
||||
* @param value 选中的地区数组 [省, 市, 区]
|
||||
*/
|
||||
function onRegionsChange(value: string[]) {
|
||||
const [province, city, district] = isEmpty(value) ? ["", "", ""] : value;
|
||||
|
||||
formData.value.province = province;
|
||||
formData.value.city = city;
|
||||
formData.value.district = district;
|
||||
}
|
||||
|
||||
onReady(() => {
|
||||
if (props.id != "") {
|
||||
getInfo();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
221
cool-unix/pages/template/shop/address.uvue
Normal file
221
cool-unix/pages/template/shop/address.uvue
Normal file
@@ -0,0 +1,221 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<view
|
||||
class="flex flex-col bg-white rounded-2xl p-4 mb-3 dark:!bg-surface-800"
|
||||
:class="{
|
||||
'!mb-0': index == addressList.length - 1
|
||||
}"
|
||||
v-for="(item, index) in addressList"
|
||||
:key="item.id"
|
||||
>
|
||||
<view class="flex flex-col">
|
||||
<cl-text color="info" :pt="{ className: '!text-sm' }"
|
||||
>{{ item.province }} {{ item.city }} {{ item.district }}</cl-text
|
||||
>
|
||||
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'my-1'
|
||||
}"
|
||||
>{{ item.address }}</cl-text
|
||||
>
|
||||
|
||||
<view class="flex flex-row">
|
||||
<cl-text :pt="{ className: '!text-sm' }">{{ item.contact }}</cl-text>
|
||||
<cl-text color="info" :pt="{ className: 'ml-3 !text-sm' }">{{
|
||||
item.phone
|
||||
}}</cl-text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="flex flex-row border border-solid border-gray-100 border-b-0 border-l-0 border-r-0 pt-4 mt-4 dark:!border-surface-700"
|
||||
>
|
||||
<cl-radio
|
||||
v-model="defaultId"
|
||||
active-icon="checkbox-circle-fill"
|
||||
inactive-icon="checkbox-blank-circle-line"
|
||||
:pt="{
|
||||
className: 'max-w-[300rpx]',
|
||||
label: {
|
||||
className: '!text-sm'
|
||||
},
|
||||
icon: {
|
||||
size: 32
|
||||
}
|
||||
}"
|
||||
:value="item.id"
|
||||
@change="onDefaultChange(item)"
|
||||
>{{ item.isDefault ? t("已设为默认") : t("设为默认") }}</cl-radio
|
||||
>
|
||||
|
||||
<view
|
||||
class="flex flex-row items-center justify-center ml-auto"
|
||||
@tap="onDelete(item.id!)"
|
||||
>
|
||||
<cl-icon name="delete-bin-line" :size="28"></cl-icon>
|
||||
<cl-text :pt="{ className: 'ml-2 !text-sm' }">{{ t("删除") }}</cl-text>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="flex flex-row items-center justify-center ml-6"
|
||||
@tap="toEdit(item.id!)"
|
||||
>
|
||||
<cl-icon name="edit-line" :size="28"></cl-icon>
|
||||
<cl-text :pt="{ className: 'ml-2 !text-sm' }">{{ t("修改") }}</cl-text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<cl-empty v-if="list.length == 0"></cl-empty>
|
||||
</view>
|
||||
|
||||
<cl-footer>
|
||||
<cl-button @tap="toAdd()">{{ t("添加地址") }}</cl-button>
|
||||
</cl-footer>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
import { parse, request, router, usePager, type Response } from "@/cool";
|
||||
import { t } from "@/locale";
|
||||
import { computed, ref } from "vue";
|
||||
import type { UserAddress } from "../types";
|
||||
|
||||
const ui = useUi();
|
||||
|
||||
const { refresh, list, loadMore } = usePager(async (data, { render }) => {
|
||||
await request({
|
||||
url: "/app/user/address/page",
|
||||
method: "POST",
|
||||
data
|
||||
})
|
||||
.then((res) => {
|
||||
if (res != null) {
|
||||
render(res);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
ui.showToast({
|
||||
message: (err as Response).message!
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
ui.hideLoading();
|
||||
});
|
||||
});
|
||||
|
||||
// 默认地址id
|
||||
const defaultId = ref<number>(0);
|
||||
|
||||
// 地址列表数据
|
||||
const addressList = computed(() =>
|
||||
list.value.map((e) => {
|
||||
e["isDefault"] = e["isDefault"] == 1 ? true : false;
|
||||
|
||||
const d = parse<UserAddress>(e)!;
|
||||
|
||||
if (d.isDefault) {
|
||||
defaultId.value = d.id!;
|
||||
}
|
||||
|
||||
return d;
|
||||
})
|
||||
);
|
||||
|
||||
// 添加地址
|
||||
function toAdd() {
|
||||
router.to("/pages/template/shop/address-edit");
|
||||
}
|
||||
|
||||
// 编辑地址
|
||||
function toEdit(id: number) {
|
||||
router.push({
|
||||
path: "/pages/template/shop/address-edit",
|
||||
query: { id }
|
||||
});
|
||||
}
|
||||
|
||||
// 删除地址
|
||||
function onDelete(id: number) {
|
||||
ui.showConfirm({
|
||||
title: t("提示"),
|
||||
message: t("删除地址后无法恢复,确认要删除该地址吗?"),
|
||||
callback: (action) => {
|
||||
if (action == "confirm") {
|
||||
request({
|
||||
url: "/app/user/address/delete",
|
||||
method: "POST",
|
||||
data: { ids: [id] }
|
||||
})
|
||||
.then(() => {
|
||||
ui.showToast({
|
||||
message: t("删除成功")
|
||||
});
|
||||
|
||||
refresh({});
|
||||
})
|
||||
.catch((err) => {
|
||||
ui.showToast({
|
||||
message: (err as Response).message!
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 设为默认地址
|
||||
function onDefaultChange(item: UserAddress) {
|
||||
// 遍历地址列表,设置选中的地址为默认地址,其他地址取消默认
|
||||
addressList.value.forEach((e) => {
|
||||
if (e.id == item.id) {
|
||||
// 切换当前地址的默认状态
|
||||
e.isDefault = !e.isDefault;
|
||||
|
||||
// 如果取消了默认,则重置默认地址ID
|
||||
if (!e.isDefault) {
|
||||
defaultId.value = 0;
|
||||
}
|
||||
} else {
|
||||
// 其他地址全部取消默认
|
||||
e.isDefault = false;
|
||||
}
|
||||
});
|
||||
|
||||
request({
|
||||
url: "/app/user/address/update",
|
||||
method: "POST",
|
||||
data: {
|
||||
id: item.id,
|
||||
isDefault: item.isDefault
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onPullDownRefresh(() => {
|
||||
refresh({ page: 1 }).finally(() => {
|
||||
uni.stopPullDownRefresh();
|
||||
});
|
||||
});
|
||||
|
||||
onReachBottom(() => {
|
||||
loadMore();
|
||||
});
|
||||
|
||||
onReady(() => {
|
||||
ui.showLoading(t("加载中"));
|
||||
|
||||
// 默认请求
|
||||
refresh({
|
||||
page: 1,
|
||||
size: 20
|
||||
});
|
||||
|
||||
onPageShow(() => {
|
||||
refresh({});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
335
cool-unix/pages/template/shop/goods-category.uvue
Normal file
335
cool-unix/pages/template/shop/goods-category.uvue
Normal file
@@ -0,0 +1,335 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="flex flex-col h-full">
|
||||
<view class="flex flex-row p-3">
|
||||
<cl-input
|
||||
:pt="{
|
||||
className: parseClass(['flex-1 !border-2 !rounded-xl'])
|
||||
}"
|
||||
prefix-icon="search-line"
|
||||
placeholder="iPhone 16 Pro Max"
|
||||
></cl-input>
|
||||
</view>
|
||||
|
||||
<view class="flex flex-row flex-1">
|
||||
<!-- 左侧分类列表 -->
|
||||
<view class="h-full w-[200rpx] bg-white dark:bg-surface-800 mr-2 rounded-tr-xl">
|
||||
<scroll-view direction="vertical" :show-scrollbar="false" class="h-full">
|
||||
<view
|
||||
class="h-[100rpx] p-2"
|
||||
v-for="(item, index) in list"
|
||||
:key="item.label"
|
||||
@click="onCategoryChange(index)"
|
||||
>
|
||||
<view
|
||||
class="flex flex-row items-center justify-center h-full rounded-lg"
|
||||
:class="[
|
||||
categoryActive == index
|
||||
? isDark
|
||||
? 'bg-primary-500'
|
||||
: 'bg-primary-50'
|
||||
: ''
|
||||
]"
|
||||
>
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: parseClass([
|
||||
[
|
||||
categoryActive == index,
|
||||
isDark ? '!text-white' : '!text-primary-500',
|
||||
isDark ? '!text-surface-300' : '!text-surface-500'
|
||||
]
|
||||
])
|
||||
}"
|
||||
>{{ item.label }}</cl-text
|
||||
>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 右侧商品列表 -->
|
||||
<view class="flex-1">
|
||||
<scroll-view
|
||||
direction="vertical"
|
||||
:scroll-into-view="scrollIntoView"
|
||||
:scroll-with-animation="!scrollLock"
|
||||
class="h-full"
|
||||
@scroll="onScroll"
|
||||
>
|
||||
<view class="pr-2">
|
||||
<view
|
||||
class="category-item flex rounded-xl bg-white dark:bg-surface-800 mb-2 pb-3"
|
||||
v-for="(item, index) in list"
|
||||
:key="item.label"
|
||||
:id="`category-item-${index}`"
|
||||
>
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'p-3'
|
||||
}"
|
||||
>{{ item.label }}</cl-text
|
||||
>
|
||||
|
||||
<view class="px-1">
|
||||
<cl-row :gutter="10">
|
||||
<cl-col
|
||||
v-for="goods in item.list"
|
||||
:key="goods.name"
|
||||
:span="8"
|
||||
>
|
||||
<view class="flex items-center flex-col justify-center">
|
||||
<cl-image :src="goods.image"></cl-image>
|
||||
<cl-text
|
||||
:ellipsis="true"
|
||||
:pt="{ className: '!text-xs text-center mt-2' }"
|
||||
>{{ goods.name }}</cl-text
|
||||
>
|
||||
</view>
|
||||
</cl-col>
|
||||
</cl-row>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { isDark, parseClass } from "@/cool";
|
||||
import { getCurrentInstance, ref } from "vue";
|
||||
|
||||
const { proxy } = getCurrentInstance()!;
|
||||
|
||||
// 商品类型
|
||||
type Goods = {
|
||||
name: string;
|
||||
price: number;
|
||||
image: string;
|
||||
};
|
||||
|
||||
// 分类类型
|
||||
type Category = {
|
||||
label: string;
|
||||
top?: number;
|
||||
list: Goods[];
|
||||
};
|
||||
|
||||
// 商品分类示例数据
|
||||
const list = ref<Category[]>([
|
||||
{
|
||||
label: "推荐",
|
||||
list: [
|
||||
{
|
||||
name: "iPhone 15 Pro",
|
||||
price: 8999,
|
||||
image: "/static/goods/iphone15pro.png"
|
||||
},
|
||||
{
|
||||
name: "华为 Mate 60 Pro",
|
||||
price: 6999,
|
||||
image: "/static/goods/mate60pro.png"
|
||||
},
|
||||
{
|
||||
name: "小米 14 Ultra",
|
||||
price: 5999,
|
||||
image: "/static/goods/xiaomi14ultra.png"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Apple",
|
||||
list: [
|
||||
{
|
||||
name: "iPhone 15 Pro",
|
||||
price: 8999,
|
||||
image: "/static/goods/iphone15pro.png"
|
||||
},
|
||||
{
|
||||
name: "iPhone 14",
|
||||
price: 5999,
|
||||
image: "/static/goods/iphone14.png"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "华为",
|
||||
list: [
|
||||
{
|
||||
name: "华为 Mate 60 Pro",
|
||||
price: 6999,
|
||||
image: "/static/goods/mate60pro.png"
|
||||
},
|
||||
{
|
||||
name: "华为 P60",
|
||||
price: 4999,
|
||||
image: "/static/goods/p60.png"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "小米",
|
||||
list: [
|
||||
{
|
||||
name: "小米 14 Ultra",
|
||||
price: 5999,
|
||||
image: "/static/goods/xiaomi14ultra.png"
|
||||
},
|
||||
{
|
||||
name: "小米 13",
|
||||
price: 3999,
|
||||
image: "/static/goods/xiaomi13.png"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "三星",
|
||||
list: [
|
||||
{
|
||||
name: "三星 Galaxy S24",
|
||||
price: 7999,
|
||||
image: "/static/goods/galaxys24.png"
|
||||
},
|
||||
{
|
||||
name: "三星 Galaxy Z Flip5",
|
||||
price: 8999,
|
||||
image: "/static/goods/galaxyzflip5.png"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "OPPO",
|
||||
list: [
|
||||
{
|
||||
name: "OPPO Find X7",
|
||||
price: 4999,
|
||||
image: "/static/goods/findx7.png"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "VIVO",
|
||||
list: [
|
||||
{
|
||||
name: "VIVO X100 Pro",
|
||||
price: 5999,
|
||||
image: "/static/goods/x100pro.png"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "荣耀",
|
||||
list: [
|
||||
{
|
||||
name: "荣耀 Magic6",
|
||||
price: 4599,
|
||||
image: "/static/goods/magic6.png"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "一加",
|
||||
list: [
|
||||
{
|
||||
name: "一加 12",
|
||||
price: 4299,
|
||||
image: "/static/goods/oneplus12.png"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "红米",
|
||||
list: [
|
||||
{
|
||||
name: "红米 K70 Pro",
|
||||
price: 3299,
|
||||
image: "/static/goods/k70pro.png"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "魅族",
|
||||
list: [
|
||||
{
|
||||
name: "魅族 21",
|
||||
price: 3999,
|
||||
image: "/static/goods/meizu21.png"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "坚果",
|
||||
list: [
|
||||
{
|
||||
name: "坚果 R2",
|
||||
price: 2999,
|
||||
image: "/static/goods/nutR2.png"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "其他",
|
||||
list: [
|
||||
{
|
||||
name: "诺基亚 X30",
|
||||
price: 2599,
|
||||
image: "/static/goods/nokiax30.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
// 滚动到指定分类
|
||||
const scrollIntoView = ref("");
|
||||
|
||||
// 滚动锁定
|
||||
const scrollLock = ref(false);
|
||||
|
||||
// 当前选中的分类
|
||||
const categoryActive = ref(0);
|
||||
|
||||
// 分类切换
|
||||
function onCategoryChange(index: number) {
|
||||
categoryActive.value = index;
|
||||
|
||||
scrollIntoView.value = `category-item-${index}`;
|
||||
scrollLock.value = true;
|
||||
|
||||
setTimeout(() => {
|
||||
scrollLock.value = false;
|
||||
}, 350);
|
||||
}
|
||||
|
||||
// 滚动时,更新当前选中的分类
|
||||
function onScroll(e: UniScrollEvent) {
|
||||
if (scrollLock.value) return;
|
||||
|
||||
const top = e.detail.scrollTop;
|
||||
|
||||
list.value.forEach((e, i) => {
|
||||
if (top >= e.top!) {
|
||||
categoryActive.value = i;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 初始化
|
||||
function init() {
|
||||
uni.createSelectorQuery()
|
||||
.in(proxy!.$root)
|
||||
.selectAll(".category-item")
|
||||
.boundingClientRect((res) => {
|
||||
(res as NodeInfo[]).forEach((e, i, arr) => {
|
||||
list.value[i].top = (e.top ?? 0) - (arr[0].top ?? 0);
|
||||
});
|
||||
})
|
||||
.exec();
|
||||
}
|
||||
|
||||
onReady(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
81
cool-unix/pages/template/shop/goods-detail/comment.uvue
Normal file
81
cool-unix/pages/template/shop/goods-detail/comment.uvue
Normal file
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<view class="flex">
|
||||
<view class="flex flex-row items-center mb-3">
|
||||
<cl-text>买家评论 78</cl-text>
|
||||
|
||||
<cl-icon name="arrow-right-s-line" :pt="{ className: 'ml-auto' }"></cl-icon>
|
||||
</view>
|
||||
|
||||
<view class="flex flex-col">
|
||||
<view class="flex flex-row my-3" v-for="item in list" :key="item.id">
|
||||
<cl-avatar :size="72" rounded :src="item.avatar"></cl-avatar>
|
||||
|
||||
<view class="flex-1 ml-4">
|
||||
<view class="flex flex-row items-center justify-between">
|
||||
<cl-text>{{ item.name }}</cl-text>
|
||||
<cl-text color="info" :pt="{ className: 'text-xs' }">{{
|
||||
item.time
|
||||
}}</cl-text>
|
||||
</view>
|
||||
|
||||
<cl-text ellipsis :lines="2" :pt="{ className: 'mt-1 text-sm' }">{{
|
||||
item.content
|
||||
}}</cl-text>
|
||||
</view>
|
||||
|
||||
<view class="ml-3 relative">
|
||||
<cl-image
|
||||
:height="100"
|
||||
:width="100"
|
||||
:pt="{
|
||||
inner: {
|
||||
className: '!rounded-lg'
|
||||
}
|
||||
}"
|
||||
src="https://unix.cool-js.com/images/demo/bg1.png"
|
||||
/>
|
||||
|
||||
<view class="bg-black/60 rounded-full px-1 absolute top-9 right-1">
|
||||
<text class="text-xs text-white">+3</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
|
||||
type Comment = {
|
||||
id: number;
|
||||
avatar: string;
|
||||
name: string;
|
||||
content: string;
|
||||
time: string;
|
||||
};
|
||||
|
||||
const list = ref<Comment[]>([
|
||||
{
|
||||
id: 1,
|
||||
avatar: "https://unix.cool-js.com/images/demo/avatar-1.jpg",
|
||||
name: "李明",
|
||||
content: "导游讲解很专业,风景绝美,是一次难忘的旅行体验!",
|
||||
time: "几分钟前"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
avatar: "https://unix.cool-js.com/images/demo/avatar-2.jpg",
|
||||
name: "小芳",
|
||||
content: "酒店干净卫生,位置也很方便,强烈推荐给大家!",
|
||||
time: "1小时前"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
avatar: "https://unix.cool-js.com/images/demo/avatar-3.jpg",
|
||||
name: "王科",
|
||||
content: "行程安排合理,吃住都很满意,导游态度超级好。",
|
||||
time: "5天前"
|
||||
}
|
||||
]);
|
||||
</script>
|
||||
15
cool-unix/pages/template/shop/goods-detail/desc.uvue
Normal file
15
cool-unix/pages/template/shop/goods-detail/desc.uvue
Normal file
@@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<view class="flex">
|
||||
<image v-for="item in list" :key="item" :src="item" mode="widthFix" class="w-full" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
|
||||
const list = ref<string[]>([
|
||||
"https://unix.cool-js.com/images/demo/goods/desc-1.jpg",
|
||||
"https://unix.cool-js.com/images/demo/goods/desc-2.jpg",
|
||||
"https://unix.cool-js.com/images/demo/goods/desc-3.jpg"
|
||||
]);
|
||||
</script>
|
||||
116
cool-unix/pages/template/shop/goods-detail/index.uvue
Normal file
116
cool-unix/pages/template/shop/goods-detail/index.uvue
Normal file
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<Topbar />
|
||||
|
||||
<cl-banner
|
||||
:list="bannerList"
|
||||
:height="800"
|
||||
:pt="{
|
||||
className: '!rounded-none',
|
||||
image: {
|
||||
className: '!rounded-none'
|
||||
}
|
||||
}"
|
||||
/>
|
||||
|
||||
<view class="card dark:!bg-surface-700" id="info">
|
||||
<Info />
|
||||
</view>
|
||||
|
||||
<view class="card !py-1 dark:!bg-surface-700">
|
||||
<view class="row is-border dark:!border-surface-600">
|
||||
<cl-icon name="apps-line"></cl-icon>
|
||||
|
||||
<view class="flex-1 ml-3">
|
||||
<cl-text>已选:黑色 128GB + 碎屏险</cl-text>
|
||||
</view>
|
||||
|
||||
<cl-icon name="arrow-right-s-line"></cl-icon>
|
||||
</view>
|
||||
|
||||
<view class="row is-border !items-start dark:!border-surface-600">
|
||||
<cl-icon name="truck-line" :pt="{ className: '!mt-1' }"></cl-icon>
|
||||
|
||||
<view class="flex-1 ml-3">
|
||||
<cl-text color="info">预计11月1日 周三 送达</cl-text>
|
||||
<cl-text color="info" :pt="{ className: '!mt-1' }">深圳益田假日广场</cl-text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="row">
|
||||
<cl-icon name="shield-check-line"></cl-icon>
|
||||
|
||||
<view class="flex-1 ml-3">
|
||||
<cl-text color="info">7天无理由退货 · 正品保证 · 极速发货 · 无忧售后</cl-text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card dark:!bg-surface-700" id="comment">
|
||||
<Comment />
|
||||
</view>
|
||||
|
||||
<view class="card !p-0 dark:!bg-surface-700" id="desc">
|
||||
<Desc />
|
||||
</view>
|
||||
|
||||
<cl-footer>
|
||||
<view class="flex flex-row overflow-visible">
|
||||
<view class="flex flex-row mr-auto overflow-visible">
|
||||
<view class="flex justify-center items-center px-4">
|
||||
<cl-icon name="heart-line" :size="42"></cl-icon>
|
||||
<cl-text :pt="{ className: 'text-xs mt-1' }">收藏</cl-text>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="flex justify-center items-center px-4 overflow-visible"
|
||||
@tap="router.to('/pages/template/shop/shopping-cart')"
|
||||
>
|
||||
<cl-icon name="shopping-cart-line" :size="42"></cl-icon>
|
||||
<cl-text :pt="{ className: 'text-xs mt-1' }">购物车</cl-text>
|
||||
|
||||
<cl-badge
|
||||
type="error"
|
||||
:value="3"
|
||||
position
|
||||
:pt="{ className: '!right-[24rpx] !top-[-10rpx] !scale-80' }"
|
||||
></cl-badge>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<cl-button text border :pt="{ className: '!w-[220rpx]' }">加入购物车</cl-button>
|
||||
<cl-button :pt="{ className: '!w-[220rpx]' }">立即购买</cl-button>
|
||||
</view>
|
||||
</cl-footer>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { router } from "@/cool";
|
||||
import { ref } from "vue";
|
||||
import Comment from "./comment.uvue";
|
||||
import Info from "./info.uvue";
|
||||
import Desc from "./desc.uvue";
|
||||
import Topbar from "./topbar.uvue";
|
||||
|
||||
const bannerList = ref<string[]>([
|
||||
"https://unix.cool-js.com/images/demo/goods/banner-1.jpg",
|
||||
"https://unix.cool-js.com/images/demo/goods/banner-2.jpg",
|
||||
"https://unix.cool-js.com/images/demo/goods/banner-3.jpg",
|
||||
"https://unix.cool-js.com/images/demo/goods/banner-4.jpg"
|
||||
]);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card {
|
||||
@apply p-4 bg-white mb-3;
|
||||
|
||||
.row {
|
||||
@apply flex flex-row items-center py-4;
|
||||
|
||||
&.is-border {
|
||||
@apply border-b border-t-0 border-l-0 border-r-0 border-solid border-surface-100;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
34
cool-unix/pages/template/shop/goods-detail/info.uvue
Normal file
34
cool-unix/pages/template/shop/goods-detail/info.uvue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<view class="flex">
|
||||
<cl-text :pt="{ className: 'text-lg font-bold mb-3' }"
|
||||
>Apple/苹果 iPhone 15 (A3092) 128GB 黑色 支持移动联通电信5G 双卡双待手机</cl-text
|
||||
>
|
||||
|
||||
<view class="flex flex-row items-center mb-3">
|
||||
<view class="flex flex-row items-end">
|
||||
<cl-text color="error" :pt="{ className: 'font-bold text-sm' }">¥</cl-text>
|
||||
<cl-text
|
||||
type="amount"
|
||||
:value="8499"
|
||||
color="error"
|
||||
currency=""
|
||||
:pt="{
|
||||
className: 'font-bold ml-1 text-2xl'
|
||||
}"
|
||||
>
|
||||
</cl-text>
|
||||
</view>
|
||||
|
||||
<cl-text color="info" :pt="{ className: 'text-sm ml-auto' }" rounded
|
||||
>已售 100 件</cl-text
|
||||
>
|
||||
</view>
|
||||
|
||||
<view class="flex flex-row mb-3">
|
||||
<cl-tag type="error" plain>满199减50</cl-tag>
|
||||
<cl-tag type="error" plain>满299减100</cl-tag>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
124
cool-unix/pages/template/shop/goods-detail/topbar.uvue
Normal file
124
cool-unix/pages/template/shop/goods-detail/topbar.uvue
Normal file
@@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<cl-sticky>
|
||||
<cl-topbar fixed safe-area-top>
|
||||
<cl-tabs
|
||||
:list="list"
|
||||
v-model="active"
|
||||
height="36px"
|
||||
:gutter="20"
|
||||
@change="onTabChange"
|
||||
></cl-tabs>
|
||||
|
||||
<template #append>
|
||||
<view class="h-[44px] w-[30px] flex items-center justify-center mr-1">
|
||||
<cl-icon name="search-line"></cl-icon>
|
||||
</view>
|
||||
</template>
|
||||
</cl-topbar>
|
||||
</cl-sticky>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { debounce, getSafeAreaHeight, isNull } from "@/cool";
|
||||
import { usePage, type ClTabsItem } from "@/uni_modules/cool-ui";
|
||||
import { getCurrentInstance, onMounted, onUnmounted, ref } from "vue";
|
||||
|
||||
const { proxy } = getCurrentInstance()!;
|
||||
const page = usePage();
|
||||
|
||||
// 当前激活tab
|
||||
const active = ref("info");
|
||||
|
||||
// 滚动时激活tab
|
||||
const scrollActive = ref("");
|
||||
|
||||
// 卡片距离顶部偏移量
|
||||
const tops = ref<number[]>([]);
|
||||
|
||||
// tab项列表
|
||||
const list = ref<ClTabsItem[]>([
|
||||
{ label: "商品", value: "info" },
|
||||
{ label: "评价", value: "comment" },
|
||||
{ label: "详情", value: "desc" }
|
||||
]);
|
||||
|
||||
/**
|
||||
* 获取所有.card顶部坐标
|
||||
*/
|
||||
async function getTops(): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
uni.createSelectorQuery()
|
||||
.in(proxy?.$root)
|
||||
.selectAll(".card")
|
||||
.boundingClientRect((res) => {
|
||||
const top = page.getScrollTop() - 44 - getSafeAreaHeight("top"); // 去头部高度
|
||||
|
||||
// 只计算有id的card
|
||||
tops.value = (res as NodeInfo[])
|
||||
.filter((e) => e.id != "" && !isNull(e.id))
|
||||
.map((e) => (e.top ?? 0) + top);
|
||||
|
||||
resolve();
|
||||
})
|
||||
.exec();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* tab切换
|
||||
*/
|
||||
async function onTabChange(value: string) {
|
||||
// 设置滚动时激活tab
|
||||
scrollActive.value = value;
|
||||
|
||||
// 重新获取卡片位置
|
||||
await getTops();
|
||||
|
||||
// 查找符合当前位置的tab索引
|
||||
const index = list.value.findIndex((e) => e.value == value);
|
||||
if (index < 0) return;
|
||||
|
||||
// 滚动到对应卡片位置
|
||||
page.scrollTo(tops.value[index] + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步当前tab
|
||||
*/
|
||||
const setActive = debounce(() => {
|
||||
active.value = scrollActive.value;
|
||||
}, 100);
|
||||
|
||||
/**
|
||||
* 滚动时激活tab
|
||||
*/
|
||||
function onScroll(top: number) {
|
||||
let index = -1;
|
||||
|
||||
// 查找符合当前位置的tab索引
|
||||
for (let i = 0; i < tops.value.length; i++) {
|
||||
if (top >= tops.value[i]) {
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
|
||||
// 设置激活tab
|
||||
if (index >= 0 && index < list.value.length) {
|
||||
scrollActive.value = list.value[index].value as string;
|
||||
setActive();
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 获取卡片位置
|
||||
getTops();
|
||||
|
||||
// 监听页面滚动
|
||||
page.onScroll(onScroll);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
// 移除监听
|
||||
page.offScroll(onScroll);
|
||||
});
|
||||
</script>
|
||||
416
cool-unix/pages/template/shop/shopping-cart.uvue
Normal file
416
cool-unix/pages/template/shop/shopping-cart.uvue
Normal file
@@ -0,0 +1,416 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<cl-sticky>
|
||||
<cl-topbar fixed safe-area-top :title="`${$t('购物车 ({num})', { num: list.length })}`">
|
||||
<template #prepend>
|
||||
<!-- #ifdef MP -->
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'ml-1'
|
||||
}"
|
||||
@tap="isDel = !isDel"
|
||||
>
|
||||
{{ isDel ? t("完成") : t("管理") }}
|
||||
</cl-text>
|
||||
<!-- #endif -->
|
||||
</template>
|
||||
|
||||
<template #append>
|
||||
<!-- #ifndef MP -->
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'mr-3'
|
||||
}"
|
||||
@tap="isDel = !isDel"
|
||||
>
|
||||
{{ isDel ? t("完成") : t("管理") }}
|
||||
</cl-text>
|
||||
<!-- #endif -->
|
||||
</template>
|
||||
</cl-topbar>
|
||||
</cl-sticky>
|
||||
|
||||
<view class="p-3">
|
||||
<view class="p-3 rounded-xl bg-white dark:!bg-surface-800 mb-3">
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'text-sm'
|
||||
}"
|
||||
>🔥 最新降价商品,限时优惠,抓紧抢购!</cl-text
|
||||
>
|
||||
</view>
|
||||
|
||||
<cl-list-item
|
||||
v-for="(item, index) in list"
|
||||
:key="item.id"
|
||||
:pt="{
|
||||
className: parseClass([
|
||||
'rounded-2xl ',
|
||||
[index == list.length - 1, 'mb-0', 'mb-3']
|
||||
]),
|
||||
inner: {
|
||||
className: '!p-4'
|
||||
}
|
||||
}"
|
||||
swipeable
|
||||
>
|
||||
<view class="flex flex-row flex-1">
|
||||
<view class="flex flex-col mr-4 pt-[55rpx]" @tap="selectItem(item)">
|
||||
<cl-icon
|
||||
name="checkbox-circle-line"
|
||||
color="primary"
|
||||
:size="40"
|
||||
v-if="item.checked"
|
||||
></cl-icon>
|
||||
<cl-icon name="checkbox-blank-circle-line" :size="40" v-else></cl-icon>
|
||||
</view>
|
||||
|
||||
<cl-image :width="150" :height="150" :src="item.cover"></cl-image>
|
||||
|
||||
<view class="flex flex-col ml-3 flex-1">
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'mb-2 font-bold'
|
||||
}"
|
||||
>{{ item.name }}</cl-text
|
||||
>
|
||||
|
||||
<view class="flex flex-row mb-2">
|
||||
<view
|
||||
class="bg-surface-100 dark:!bg-surface-700 rounded-md py-1 px-2 flex flex-row items-center"
|
||||
>
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'text-xs'
|
||||
}"
|
||||
>{{ item.skuName }}</cl-text
|
||||
>
|
||||
<cl-icon name="arrow-down-s-line"></cl-icon>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="flex flex-row items-center mb-2">
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'text-[22rpx] text-red-500 mr-[1rpx]'
|
||||
}"
|
||||
>¥</cl-text
|
||||
>
|
||||
<cl-text
|
||||
:pt="{
|
||||
className: 'text-red-500 text-[32rpx] mr-auto'
|
||||
}"
|
||||
>{{ item.price }}</cl-text
|
||||
>
|
||||
|
||||
<view
|
||||
v-if="isDel"
|
||||
class="p-[8rpx] bg-red-500 rounded-lg"
|
||||
@tap="delItem(index)"
|
||||
>
|
||||
<cl-icon name="delete-bin-line" color="white" :size="24"></cl-icon>
|
||||
</view>
|
||||
|
||||
<view class="flex" v-else>
|
||||
<cl-input-number
|
||||
v-model="item.count"
|
||||
:size="40"
|
||||
:min="1"
|
||||
:pt="{
|
||||
op: {
|
||||
plus: {
|
||||
className: '!rounded-full'
|
||||
},
|
||||
minus: {
|
||||
className: '!rounded-full'
|
||||
},
|
||||
icon: {
|
||||
size: 28
|
||||
}
|
||||
},
|
||||
value: {
|
||||
className: '!w-[60rpx] rounded-full !px-0',
|
||||
input: {
|
||||
className: 'text-[24rpx]'
|
||||
}
|
||||
}
|
||||
}"
|
||||
></cl-input-number>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<cl-text :size="22" color="error">比加入时降¥100</cl-text>
|
||||
|
||||
<cl-text :size="22">满1件可换购0.5元商品</cl-text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<template #swipe-right>
|
||||
<cl-button
|
||||
type="error"
|
||||
:pt="{
|
||||
className: '!rounded-none h-full w-[160rpx]'
|
||||
}"
|
||||
@tap="delItem(index)"
|
||||
>{{ t("删除") }}</cl-button
|
||||
>
|
||||
</template>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-empty v-if="list.length == 0"></cl-empty>
|
||||
</view>
|
||||
|
||||
<cl-footer>
|
||||
<view class="flex flex-row items-center h-[70rpx]">
|
||||
<cl-checkbox
|
||||
active-icon="checkbox-circle-line"
|
||||
inactive-icon="checkbox-blank-circle-line"
|
||||
:pt="{
|
||||
className: 'mr-auto'
|
||||
}"
|
||||
:size="28"
|
||||
v-model="selectAll"
|
||||
@change="onSelectAllChange"
|
||||
>{{ t("全选") }}</cl-checkbox
|
||||
>
|
||||
|
||||
<template v-if="isDel">
|
||||
<cl-button
|
||||
type="error"
|
||||
:pt="{
|
||||
className: '!px-5'
|
||||
}"
|
||||
@tap="delAll"
|
||||
>
|
||||
{{ t("删除") }}
|
||||
</cl-button>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<view class="flex flex-col mr-3 items-end pt-1">
|
||||
<cl-text
|
||||
color="info"
|
||||
:pt="{
|
||||
className: 'text-xs'
|
||||
}"
|
||||
>{{ t("合计") }}</cl-text
|
||||
>
|
||||
<cl-text color="error" :value="totalPrice" type="amount"></cl-text>
|
||||
</view>
|
||||
|
||||
<cl-button
|
||||
type="error"
|
||||
:pt="{
|
||||
className: '!px-8'
|
||||
}"
|
||||
@tap="toSettle"
|
||||
>
|
||||
{{ t("去结算") }}
|
||||
</cl-button>
|
||||
</template>
|
||||
</view>
|
||||
</cl-footer>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { parseClass, isEmpty } from "@/cool";
|
||||
import { $t, t } from "@/locale";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
import { computed, ref } from "vue";
|
||||
|
||||
const ui = useUi();
|
||||
|
||||
// 商品类型
|
||||
type Goods = {
|
||||
id: number;
|
||||
name: string;
|
||||
count: number;
|
||||
price: number;
|
||||
cover: string;
|
||||
skuName: string;
|
||||
checked: boolean;
|
||||
};
|
||||
|
||||
// 商品列表
|
||||
const list = ref<Goods[]>([
|
||||
{
|
||||
id: 1,
|
||||
name: "Apple iPad",
|
||||
count: 1,
|
||||
price: 450,
|
||||
cover: "https://img.yzcdn.cn/vant/ipad.png",
|
||||
skuName: "深空灰色 128GB WLAN版",
|
||||
checked: true
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Samsung Galaxy S24",
|
||||
count: 2,
|
||||
price: 699,
|
||||
cover: "https://img.yzcdn.cn/vant/samsung.png",
|
||||
skuName: "曜石黑 12GB+256GB 官方标配",
|
||||
checked: false
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Sony WH-1000XM5",
|
||||
count: 1,
|
||||
price: 299,
|
||||
cover: "https://img.yzcdn.cn/vant/sony.png",
|
||||
skuName: "黑色 无线蓝牙 官方标配",
|
||||
checked: false
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "小米手环8",
|
||||
count: 3,
|
||||
price: 49,
|
||||
cover: "https://img.yzcdn.cn/vant/xiaomi.png",
|
||||
skuName: "曜石黑 标准版 硅胶表带",
|
||||
checked: false
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "Kindle Paperwhite",
|
||||
count: 1,
|
||||
price: 120,
|
||||
cover: "https://img.yzcdn.cn/vant/kindle.png",
|
||||
skuName: "黑色 8GB 官方标配",
|
||||
checked: false
|
||||
}
|
||||
]);
|
||||
|
||||
// 是否全选
|
||||
const selectAll = ref(false);
|
||||
|
||||
/**
|
||||
* 选择/取消选择单个商品
|
||||
* @param item 需要操作的商品
|
||||
*/
|
||||
function selectItem(item: Goods) {
|
||||
// 切换选中状态
|
||||
item.checked = !item.checked;
|
||||
// 判断是否所有商品都被选中,更新全选状态
|
||||
selectAll.value = list.value.every((item) => item.checked);
|
||||
}
|
||||
|
||||
/**
|
||||
* 全选/取消全选
|
||||
* @param val 是否全选
|
||||
*/
|
||||
function onSelectAllChange(val: boolean) {
|
||||
list.value.forEach((item, index, arr) => {
|
||||
// item.checked = val; // 这样写,在 android 上无效
|
||||
arr[index].checked = val;
|
||||
});
|
||||
}
|
||||
|
||||
// 是否处于删除模式
|
||||
const isDel = ref(false);
|
||||
|
||||
/**
|
||||
* 删除单个商品
|
||||
* @param index 商品索引
|
||||
*/
|
||||
function delItem(index: number) {
|
||||
ui.showConfirm({
|
||||
title: t("温馨提示"),
|
||||
message: t("确定删除该商品吗?"),
|
||||
callback(action) {
|
||||
if (action === "confirm") {
|
||||
// 删除指定索引的商品
|
||||
list.value.splice(index, 1);
|
||||
|
||||
ui.showToast({
|
||||
message: t("删除成功")
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除所有已选中的商品
|
||||
*/
|
||||
function delAll() {
|
||||
const checked = list.value.filter((item) => item.checked);
|
||||
|
||||
// 如果没有选中商品,提示用户
|
||||
if (isEmpty(checked)) {
|
||||
return ui.showToast({
|
||||
message: t("请先选择商品")
|
||||
});
|
||||
}
|
||||
|
||||
ui.showConfirm({
|
||||
title: t("温馨提示"),
|
||||
message: t("确定删除选中的商品吗?"),
|
||||
callback(action) {
|
||||
if (action == "confirm") {
|
||||
// 只保留未选中的商品
|
||||
list.value = list.value.filter((item) => !item.checked);
|
||||
|
||||
// 如果之前是全选,删除后取消全选状态
|
||||
if (selectAll.value) {
|
||||
selectAll.value = false;
|
||||
}
|
||||
|
||||
ui.showToast({
|
||||
message: t("删除成功")
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空购物车
|
||||
*/
|
||||
function clear() {
|
||||
list.value = [];
|
||||
selectAll.value = false;
|
||||
isDel.value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算已选中商品的总价
|
||||
*/
|
||||
const totalPrice = computed(() => {
|
||||
return list.value
|
||||
.filter((item) => item.checked)
|
||||
.reduce((acc, item) => acc + item.price * item.count, 0);
|
||||
});
|
||||
|
||||
/**
|
||||
* 结算操作
|
||||
*/
|
||||
function toSettle() {
|
||||
// 如果没有选中商品,提示用户
|
||||
if (totalPrice.value <= 0) {
|
||||
return ui.showToast({
|
||||
message: t("请先选择商品")
|
||||
});
|
||||
}
|
||||
|
||||
ui.showConfirm({
|
||||
title: t("温馨提示"),
|
||||
message: $t("您需支付 {price} 元,请确认支付", { price: totalPrice.value }),
|
||||
beforeClose(action, { showLoading, close }) {
|
||||
if (action == "confirm") {
|
||||
showLoading();
|
||||
|
||||
setTimeout(() => {
|
||||
ui.showToast({
|
||||
message: t("支付成功")
|
||||
});
|
||||
|
||||
close();
|
||||
}, 1000);
|
||||
} else {
|
||||
close();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
10
cool-unix/pages/template/types/index.ts
Normal file
10
cool-unix/pages/template/types/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export type UserAddress = {
|
||||
id?: number;
|
||||
contact: string;
|
||||
phone: string;
|
||||
province: string;
|
||||
city: string;
|
||||
district: string;
|
||||
address: string;
|
||||
isDefault: boolean;
|
||||
};
|
||||
134
cool-unix/pages/user/components/login/phone.uvue
Normal file
134
cool-unix/pages/user/components/login/phone.uvue
Normal file
@@ -0,0 +1,134 @@
|
||||
<template>
|
||||
<view class="flex flex-col mb-5">
|
||||
<cl-text :pt="{ className: 'text-lg font-bold' }">{{ t("手机登录") }}</cl-text>
|
||||
<cl-text :pt="{ className: 'text-sm mt-2' }" color="info">{{
|
||||
t("未注册的手机号登录成功后将自动注册")
|
||||
}}</cl-text>
|
||||
</view>
|
||||
|
||||
<view class="flex flex-col">
|
||||
<view class="mb-3 flex flex-row">
|
||||
<cl-input
|
||||
v-model="form.phone"
|
||||
prefix-icon="device-fill"
|
||||
:placeholder="t('请输入手机号')"
|
||||
:border="false"
|
||||
:pt="{
|
||||
className: parseClass([
|
||||
'!h-[90rpx] flex-1 !rounded-xl !px-4',
|
||||
[isDark, '!bg-surface-70', '!bg-white']
|
||||
]),
|
||||
prefixIcon: {
|
||||
className: 'mr-1'
|
||||
}
|
||||
}"
|
||||
></cl-input>
|
||||
</view>
|
||||
|
||||
<view class="relative flex flex-row items-center mb-5">
|
||||
<cl-input
|
||||
v-model="form.smsCode"
|
||||
:clearable="false"
|
||||
type="number"
|
||||
prefix-icon="shield-check-fill"
|
||||
:placeholder="t('请输入验证码')"
|
||||
:maxlength="4"
|
||||
:border="false"
|
||||
:pt="{
|
||||
className: parseClass([
|
||||
'!h-[90rpx] flex-1 !rounded-xl !px-4',
|
||||
[isDark, '!bg-surface-70', '!bg-white']
|
||||
]),
|
||||
prefixIcon: {
|
||||
className: 'mr-1'
|
||||
}
|
||||
}"
|
||||
>
|
||||
</cl-input>
|
||||
|
||||
<view class="absolute right-0">
|
||||
<sms-btn
|
||||
:ref="refs.set('smsBtn')"
|
||||
:phone="form.phone"
|
||||
@success="showCode = true"
|
||||
></sms-btn>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<cl-button
|
||||
:pt="{
|
||||
className: '!h-[90rpx] !rounded-xl'
|
||||
}"
|
||||
:loading="loading"
|
||||
:disabled="disabled"
|
||||
@tap="toLogin"
|
||||
>
|
||||
{{ t("登录") }}
|
||||
</cl-button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { t } from "@/locale";
|
||||
import { computed, inject, ref, type PropType } from "vue";
|
||||
import type { LoginForm } from "../../types";
|
||||
import SmsBtn from "@/components/sms-btn.uvue";
|
||||
import { isDark, parseClass, request, useRefs, type Response } from "@/cool";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
|
||||
const props = defineProps({
|
||||
form: {
|
||||
type: Object as PropType<LoginForm>,
|
||||
default: () => ({})
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(["success"]);
|
||||
|
||||
const ui = useUi();
|
||||
const refs = useRefs();
|
||||
|
||||
// 是否同意
|
||||
const isAgree = inject("isAgree") as () => boolean;
|
||||
|
||||
// 是否显示验证码
|
||||
const showCode = ref(false);
|
||||
|
||||
// 是否加载中
|
||||
const loading = ref(false);
|
||||
|
||||
// 是否禁用
|
||||
const disabled = computed(() => {
|
||||
return props.form.phone == "" || props.form.smsCode == "";
|
||||
});
|
||||
|
||||
// 登录
|
||||
async function toLogin() {
|
||||
if (!isAgree()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { phone, smsCode } = props.form;
|
||||
|
||||
loading.value = true;
|
||||
|
||||
await request({
|
||||
url: "/app/user/login/phone",
|
||||
method: "POST",
|
||||
data: {
|
||||
phone,
|
||||
smsCode
|
||||
}
|
||||
})
|
||||
.then((res) => {
|
||||
emit("success", res);
|
||||
})
|
||||
.catch((err) => {
|
||||
ui.showToast({
|
||||
message: (err as Response).message!
|
||||
});
|
||||
});
|
||||
|
||||
loading.value = false;
|
||||
}
|
||||
</script>
|
||||
242
cool-unix/pages/user/components/login/wx.uvue
Normal file
242
cool-unix/pages/user/components/login/wx.uvue
Normal file
@@ -0,0 +1,242 @@
|
||||
<template>
|
||||
<cl-popup
|
||||
v-model="editVisible"
|
||||
direction="center"
|
||||
:title="t('提示')"
|
||||
size="80%"
|
||||
@close="onEditClose"
|
||||
>
|
||||
<view class="p-4 pt-0">
|
||||
<cl-text color="info" :pt="{ className: 'text-sm' }">
|
||||
{{ t("为提供更好的服务,我们邀请您填写昵称、头像等公开信息") }}
|
||||
</cl-text>
|
||||
|
||||
<view
|
||||
class="flex flex-row justify-between items-center bg-surface-100 rounded-xl p-2 px-3 mt-3 h-[95rpx]"
|
||||
>
|
||||
<cl-text>{{ t("头像") }}</cl-text>
|
||||
|
||||
<view class="relative">
|
||||
<cl-avatar :size="60" :src="editForm.avatarUrl"></cl-avatar>
|
||||
|
||||
<button
|
||||
class="absolute top-0 right-0 h-10 w-10 z-10 opacity-0 p-0 m-0"
|
||||
open-type="chooseAvatar"
|
||||
@chooseavatar="onEditChooseAvatar"
|
||||
></button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="flex flex-row justify-between items-center bg-surface-100 rounded-xl p-2 px-3 mt-3 h-[95rpx]"
|
||||
>
|
||||
<cl-text>{{ t("昵称") }}</cl-text>
|
||||
|
||||
<cl-input
|
||||
v-model="editForm.nickName"
|
||||
type="nickname"
|
||||
:border="false"
|
||||
:placeholder="t('点击输入昵称')"
|
||||
:maxlength="16"
|
||||
:pt="{
|
||||
className: '!bg-transparent !px-0 flex-1',
|
||||
inner: {
|
||||
className: 'text-right'
|
||||
}
|
||||
}"
|
||||
></cl-input>
|
||||
</view>
|
||||
|
||||
<view class="flex flex-row mt-4">
|
||||
<cl-button
|
||||
size="large"
|
||||
text
|
||||
border
|
||||
type="light"
|
||||
:pt="{
|
||||
className: 'flex-1 !rounded-xl h-[80rpx]'
|
||||
}"
|
||||
@tap="editClose"
|
||||
>{{ t("取消") }}</cl-button
|
||||
>
|
||||
<cl-button
|
||||
size="large"
|
||||
:pt="{
|
||||
className: 'flex-1 !rounded-xl h-[80rpx]'
|
||||
}"
|
||||
:loading="editLoading"
|
||||
@tap="editSave"
|
||||
>{{ t("确认") }}</cl-button
|
||||
>
|
||||
</view>
|
||||
</view>
|
||||
</cl-popup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
parse,
|
||||
request,
|
||||
router,
|
||||
upload,
|
||||
userInfo,
|
||||
useStore,
|
||||
useWx,
|
||||
type Response,
|
||||
type Token
|
||||
} from "@/cool";
|
||||
import { t } from "@/locale";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
import { reactive, ref } from "vue";
|
||||
|
||||
const emit = defineEmits(["success"]);
|
||||
|
||||
const { user } = useStore();
|
||||
const ui = useUi();
|
||||
const wx = useWx();
|
||||
|
||||
// 是否显示编辑
|
||||
const editVisible = ref(false);
|
||||
|
||||
// 是否保存中
|
||||
const editLoading = ref(false);
|
||||
|
||||
// 编辑表单
|
||||
type EditForm = {
|
||||
avatarUrl: string;
|
||||
nickName: string;
|
||||
};
|
||||
const editForm = reactive<EditForm>({
|
||||
avatarUrl: "",
|
||||
nickName: ""
|
||||
});
|
||||
|
||||
// 编辑打开
|
||||
function editOpen() {
|
||||
editVisible.value = true;
|
||||
}
|
||||
|
||||
// 编辑关闭
|
||||
function editClose() {
|
||||
editVisible.value = false;
|
||||
}
|
||||
|
||||
// 编辑保存
|
||||
async function editSave() {
|
||||
// 校验头像是否已上传
|
||||
if (editForm.avatarUrl == "") {
|
||||
ui.showToast({
|
||||
message: t("请上传头像")
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 校验昵称是否已填写
|
||||
if (editForm.nickName == "") {
|
||||
ui.showToast({
|
||||
message: t("请输入昵称")
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置保存状态为加载中
|
||||
editLoading.value = true;
|
||||
|
||||
// 上传头像并更新用户信息
|
||||
await upload(editForm.avatarUrl)
|
||||
.then((url) => {
|
||||
// 上传成功后,更新用户昵称和头像
|
||||
user.update({
|
||||
nickName: editForm.nickName,
|
||||
avatarUrl: url
|
||||
});
|
||||
|
||||
// 关闭弹窗
|
||||
editClose();
|
||||
|
||||
// 跳转首页
|
||||
router.nextLogin();
|
||||
})
|
||||
.catch((err) => {
|
||||
// 上传失败,提示错误信息
|
||||
ui.showToast({
|
||||
message: (err as Response).message!
|
||||
});
|
||||
});
|
||||
|
||||
// 恢复保存状态
|
||||
editLoading.value = false;
|
||||
}
|
||||
|
||||
// 编辑选择头像
|
||||
function onEditChooseAvatar(e: UniEvent) {
|
||||
// #ifdef MP-WEIXIN
|
||||
editForm.avatarUrl = e.detail.avatarUrl;
|
||||
// #endif
|
||||
}
|
||||
|
||||
// 编辑关闭
|
||||
function onEditClose() {
|
||||
editVisible.value = false;
|
||||
}
|
||||
|
||||
// 微信小程序登录
|
||||
async function miniLogin() {
|
||||
// #ifdef MP
|
||||
ui.showLoading(t("登录中"));
|
||||
|
||||
await wx.miniLogin().then(async (data) => {
|
||||
await request({
|
||||
url: "/app/user/login/mini",
|
||||
method: "POST",
|
||||
data
|
||||
})
|
||||
.then(async (res) => {
|
||||
// 设置token
|
||||
user.setToken(parse<Token>(res)!);
|
||||
|
||||
// 获取用户信息
|
||||
await user.get();
|
||||
|
||||
// 是否首次注册,根据业务情况调整判断逻辑
|
||||
if (userInfo.value?.nickName == "微信用户") {
|
||||
// 打开编辑弹窗
|
||||
editOpen();
|
||||
} else {
|
||||
// 跳转首页
|
||||
router.nextLogin();
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
ui.showToast({
|
||||
message: (err as Response).message!
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
ui.hideLoading();
|
||||
// #endif
|
||||
}
|
||||
|
||||
// 微信APP登录
|
||||
function appLogin() {
|
||||
// 开发中
|
||||
}
|
||||
|
||||
// 微信登录
|
||||
async function login() {
|
||||
// #ifdef MP
|
||||
miniLogin();
|
||||
// #endif
|
||||
|
||||
// #ifdef APP
|
||||
appLogin();
|
||||
// #endif
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
login,
|
||||
editOpen,
|
||||
editClose
|
||||
});
|
||||
</script>
|
||||
52
cool-unix/pages/user/edit-description.uvue
Normal file
52
cool-unix/pages/user/edit-description.uvue
Normal file
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<cl-topbar safe-area-top :title="t('编辑简介')" background-color="transparent"> </cl-topbar>
|
||||
|
||||
<view class="p-3">
|
||||
<cl-textarea
|
||||
v-model="content"
|
||||
:placeholder="t('介绍一下自己')"
|
||||
:border="false"
|
||||
:height="200"
|
||||
>
|
||||
</cl-textarea>
|
||||
</view>
|
||||
|
||||
<cl-footer>
|
||||
<cl-button size="large" :disabled="content == ''" @tap="confirm">{{
|
||||
t("确认")
|
||||
}}</cl-button>
|
||||
</cl-footer>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { router, userInfo, useStore } from "@/cool";
|
||||
import { t } from "@/locale";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
import { ref } from "vue";
|
||||
|
||||
const ui = useUi();
|
||||
const { user } = useStore();
|
||||
|
||||
// 输入框内容
|
||||
const content = ref("");
|
||||
|
||||
async function confirm() {
|
||||
if (content.value == "") {
|
||||
return ui.showToast({
|
||||
message: t("简介不能为空")
|
||||
});
|
||||
}
|
||||
|
||||
await user.update({
|
||||
description: content.value
|
||||
});
|
||||
|
||||
router.back();
|
||||
}
|
||||
|
||||
onReady(() => {
|
||||
content.value = userInfo.value?.description ?? "";
|
||||
});
|
||||
</script>
|
||||
80
cool-unix/pages/user/edit-name.uvue
Normal file
80
cool-unix/pages/user/edit-name.uvue
Normal file
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<cl-topbar safe-area-top :title="t('编辑昵称')" background-color="transparent"> </cl-topbar>
|
||||
|
||||
<view class="p-3">
|
||||
<cl-input
|
||||
v-model="content"
|
||||
autofocus
|
||||
:placeholder="t('请输入昵称')"
|
||||
:border="false"
|
||||
:pt="{
|
||||
className: '!h-[80rpx]'
|
||||
}"
|
||||
>
|
||||
<template #append>
|
||||
<cl-text color="info" :pt="{ className: 'text-sm ml-2' }"
|
||||
>{{ content.length }}/20</cl-text
|
||||
>
|
||||
</template>
|
||||
</cl-input>
|
||||
|
||||
<view class="p-3">
|
||||
<cl-text color="info" :pt="{ className: 'text-sm' }">{{
|
||||
t("请设置2-20个字符,不包括@<>/等无效字符")
|
||||
}}</cl-text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<cl-footer>
|
||||
<cl-button size="large" :disabled="content == ''" @tap="confirm">{{
|
||||
t("确认")
|
||||
}}</cl-button>
|
||||
</cl-footer>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { router, userInfo, useStore } from "@/cool";
|
||||
import { t } from "@/locale";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
import { ref } from "vue";
|
||||
|
||||
const ui = useUi();
|
||||
const { user } = useStore();
|
||||
|
||||
// 输入框内容
|
||||
const content = ref("");
|
||||
|
||||
// 确认按钮点击事件
|
||||
async function confirm() {
|
||||
// 检查昵称长度和特殊字符
|
||||
if (content.value.length < 2 || content.value.length > 20) {
|
||||
ui.showToast({
|
||||
message: t("昵称长度需在2-20个字符之间")
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 正则匹配 - 不允许特殊字符@<>/等
|
||||
const reg = /^[^@<>\/]*$/;
|
||||
if (!reg.test(content.value)) {
|
||||
ui.showToast({
|
||||
message: t("昵称不能包含@<>/等特殊字符")
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新用户昵称
|
||||
await user.update({
|
||||
nickName: content.value
|
||||
});
|
||||
|
||||
router.back();
|
||||
}
|
||||
|
||||
// 页面加载时,设置输入框内容
|
||||
onReady(() => {
|
||||
content.value = userInfo.value?.nickName ?? "";
|
||||
});
|
||||
</script>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user