商務卡片預覽

dev
Wayne 3 years ago
parent 16a6ae2c6a
commit 86e074b5ed

2
.gitignore vendored

@ -22,3 +22,5 @@ dist-ssr
*.njsproj
*.sln
*.sw?
.env.production

@ -1,41 +0,0 @@
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<!-- <link rel="manifest" href="manifest.json"> -->
<!-- <meta name="apple-mobile-web-app-capable" content="yes"> -->
<!-- <meta name="apple-mobile-web-app-status-bar-style" content="black"> -->
<!-- <meta name="apple-mobile-web-app-title" content="UTel電子名片"> -->
<!-- <link rel="apple-touch-icon" href="icon-192x192.png" sizes="192x192"> -->
<link rel="stylesheet" href="./css/flex2html.css">
<link rel="stylesheet" href="https://at.alicdn.com/t/font_3193091_nixfhsddmnl.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous">
</head>
<body>
<noscript>
<strong>UTel電子名片</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
<script src="./js/flex2html.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/pwacompat@2.0.15/pwacompat.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-fQybjgWLrvvRgtW6bFlB7jaZrFsaBXjsOMm/tB9LTS58ONXgqbR9W8oWht/amnpF" crossorigin="anonymous"></script>
<script>
var iOS = (/iP(hone|od|ad)/.test(navigator.userAgent));
if (iOS) {
var v = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/);
var iOSversion = parseInt(v[1], 10);
if (iOSversion < 13) {
document.querySelector('link[rel="manifest"]').setAttribute("rel", "no-on-ios");
}
}
</script>
</body>
</html>

@ -21,7 +21,7 @@ const flexRef = ref(null)
onActivated(() => {
flexRef.value.innerHTML = ''
let flexContent = genCard1(JSON.parse(route.params.content))
let flexContent = genCard1(JSON.parse(history.state.content))
flex2html("flex", flexContent)
})

@ -0,0 +1,37 @@
import { defineStore } from 'pinia'
import { getUserInfo, setUserTpl, toggleSendWithAD } from "@/api";
export const useUserStore = defineStore('user', {
state: () => {
return {
userInfo: null,
}
},
getters: {
},
actions: {
async getUserInfo(context) {
var res = await getUserInfo();
if (res.code === 200) {
context.commit("setUserInfo", res.data);
}
},
// async setUserTpl(context, payload) {
// var res = await setUserTpl(payload);
// if (res.code === 200) {
// context.commit("setTpl", payload);
// }
// },
// async setSendWithAd(context, payload) {
// console.log('payload', payload);
// let res = await toggleSendWithAD(payload);
// if (res.code === 200) {
// context.commit("setSendWithAd", payload);
// return true;
// }
// return false;
// }
}
})

@ -1,15 +1,8 @@
<script>
<script setup>
import Footer from "@/components/Footer.vue";
import FlexView from "@/components/FlexView.vue";
import {
ref,
reactive,
toRefs,
watch,
onBeforeMount,
defineComponent,
} from "vue";
import { ref, reactive, watch } from "vue";
import { updateCusCard } from "@/api";
import { useStore } from "vuex";
@ -19,21 +12,13 @@ import axios from "axios";
import _ from "lodash";
import { showToast } from "vant";
import { showLoadingToast,showSuccessToast } from 'vant';
import { showLoadingToast, showSuccessToast } from "vant";
import { Cropper } from "vue-advanced-cropper";
import "vue-advanced-cropper/dist/style.css";
const URL = window.URL || window.webkitURL;
export default defineComponent({
name: "EditCard",
components: {
Footer,
Cropper,
FlexView,
},
async setup() {
const store = useStore();
const router = useRouter();
@ -47,8 +32,8 @@ export default defineComponent({
let flexRef = ref(null);
const showMvPopup = () => {
console.log('test');
showMovie = true;
console.log("test");
showMovie.value = true;
};
const crop = ref({
@ -60,17 +45,19 @@ export default defineComponent({
autoCropHeight: 200,
});
let state = reactive({
imagePath: "",
previewImage: null,
fileList: [],
showFooter: true,
form: {
// let state = reactive({
// imagePath: "",
// previewImage: null,
// fileList: [],
// showFooter: true,
// });
const form = ref({
page: 1,
title: "商務卡片",
showNfc: true,
json5: {
altText: '',
altText: "",
btnHeight: "md",
descSize: "sm",
titleSize: "xl",
@ -89,35 +76,37 @@ export default defineComponent({
},
],
},
},
});
const sizeColumns = ref([
{text:'xxs',value:'xxs'},
{text:'xs',value: 'xs'},
{text:'sm',value: 'sm'},
{text:'md',value: 'md'},
{text:'lg',value: 'lg'},
{text:'xl',value: 'xl'},
{text:'xxl',value:'xxl'},
{text:'3xl',value:'3xl'},
{text:'4xl',value:'4xl'},
{text:'5xl',value:'5xl'}
{ text: "xxs", value: "xxs" },
{ text: "xs", value: "xs" },
{ text: "sm", value: "sm" },
{ text: "md", value: "md" },
{ text: "lg", value: "lg" },
{ text: "xl", value: "xl" },
{ text: "xxl", value: "xxl" },
{ text: "3xl", value: "3xl" },
{ text: "4xl", value: "4xl" },
{ text: "5xl", value: "5xl" },
]);
if (
store.state.user.userInfo.cus_card &&
store.state.user.userInfo.cus_card.length > 0
) {
state.form = JSON.parse(_.clone(store.state.user.userInfo.cus_card));
form.value = JSON.parse(_.clone(store.state.user.userInfo.cus_card));
}
watch(()=>state.form.title,(newVal)=>{
state.form.json5.altText = newVal
})
watch(
() => form.value.title,
(newVal) => {
form.value.json5.altText = newVal;
}
);
const addCard = () => {
state.form.json5.cards.push({
form.value.json5.cards.push({
bgColor: "#ffffff",
desc: "",
descColor: "#000000",
@ -129,71 +118,70 @@ export default defineComponent({
titleColor: "#000000",
ratio: "20:13",
});
state.form.page = state.form.json5.cards.length;
form.value.page = form.value.json5.cards.length;
};
const delCard = (page) => {
if (page > 1) {
state.form.page = page - 1;
form.value.page = page - 1;
}
state.form.json5.cards.splice(page - 1, 1);
form.value.json5.cards.splice(page - 1, 1);
};
const addBtn = (page) => {
if (!state.form.json5.cards[page - 1].btns) {
state.form.json5.cards[page - 1].btns = [];
if (!form.value.json5.cards[page - 1].btns) {
form.value.json5.cards[page - 1].btns = [];
}
state.form.json5.cards[page - 1].btns.push({
form.value.json5.cards[page - 1].btns.push({
color: "#42659a",
link: "",
style: "primary",
text: "",
btnHeight: "md"
btnHeight: "md",
});
};
const addShareBtn = (page) => {
if (!state.form.json5.cards[page - 1].btns) {
state.form.json5.cards[page - 1].btns = [];
if (!form.value.json5.cards[page - 1].btns) {
form.value.json5.cards[page - 1].btns = [];
}
state.form.json5.cards[page - 1].btns.push({
form.value.json5.cards[page - 1].btns.push({
color: "#42659a",
link: `${import.meta.env.VITE_APP_SEND_URL}?userid=${sessionStorage.getItem(
"uid"
)}&cardid=2`,
style: "primary",
text: "分享好友",
btnHeight: "md"
btnHeight: "md",
});
};
const moveCard = (type, page) => {
if (type === 0) {
if (page !== 1) {
[state.form.json5.cards[page - 1], state.form.json5.cards[page - 2]] =
[
state.form.json5.cards[page - 2],
state.form.json5.cards[page - 1],
[form.value.json5.cards[page - 1], form.value.json5.cards[page - 2]] = [
form.value.json5.cards[page - 2],
form.value.json5.cards[page - 1],
];
state.form.page = page - 1;
form.value.page = page - 1;
}
} else {
if (page !== state.form.json5.cards.length) {
[state.form.json5.cards[page], state.form.json5.cards[page - 1]] = [
state.form.json5.cards[page - 1],
state.form.json5.cards[page],
if (page !== form.value.json5.cards.length) {
[form.value.json5.cards[page], form.value.json5.cards[page - 1]] = [
form.value.json5.cards[page - 1],
form.value.json5.cards[page],
];
state.form.page = page + 1;
form.value.page = page + 1;
}
}
};
const delBtn = (index) => {
state.form.json5.cards[state.form.page - 1].btns.splice(index, 1);
if (state.form.json5.cards[state.form.page - 1].btns.length === 0) {
delete state.form.json5.cards[state.form.page - 1].btns;
form.value.json5.cards[form.value.page - 1].btns.splice(index, 1);
if (form.value.json5.cards[form.value.page - 1].btns.length === 0) {
delete form.value.json5.cards[form.value.page - 1].btns;
}
};
@ -201,24 +189,21 @@ export default defineComponent({
if (type === 0) {
if (index !== 0) {
[
state.form.json5.cards[state.form.page - 1].btns[index],
state.form.json5.cards[state.form.page - 1].btns[index - 1],
form.value.json5.cards[form.value.page - 1].btns[index],
form.value.json5.cards[form.value.page - 1].btns[index - 1],
] = [
state.form.json5.cards[state.form.page - 1].btns[index - 1],
state.form.json5.cards[state.form.page - 1].btns[index],
form.value.json5.cards[form.value.page - 1].btns[index - 1],
form.value.json5.cards[form.value.page - 1].btns[index],
];
}
} else {
if (
index + 1 !==
state.form.json5.cards[state.form.page - 1].btns.length
) {
if (index + 1 !== form.value.json5.cards[form.value.page - 1].btns.length) {
[
state.form.json5.cards[state.form.page - 1].btns[index + 1],
state.form.json5.cards[state.form.page - 1].btns[index],
form.value.json5.cards[form.value.page - 1].btns[index + 1],
form.value.json5.cards[form.value.page - 1].btns[index],
] = [
state.form.json5.cards[state.form.page - 1].btns[index],
state.form.json5.cards[state.form.page - 1].btns[index + 1],
form.value.json5.cards[form.value.page - 1].btns[index],
form.value.json5.cards[form.value.page - 1].btns[index + 1],
];
}
}
@ -246,7 +231,7 @@ export default defineComponent({
);
if (res.data.code == 200) {
state.form.json5.cards[state.form.page - 1].image = res.data.data;
form.value.json5.cards[form.value.page - 1].image = res.data.data;
showSuccessToast("上傳成功");
} else {
@ -257,24 +242,26 @@ export default defineComponent({
};
const handleDelete = () => {
state.form.json5.cards[state.form.page - 1].image = "";
form.value.json5.cards[form.value.page - 1].image = "";
};
const handlePreview = () => {
router.push({
name: "CardPreview",
params: { content: JSON.stringify(state.form) },
state: { content: JSON.stringify(form.value) },
});
};
const onTitleSizeConfirm = ({ selectedOptions }) => {
showTitleSizePicker.value = false;
state.form.json5.cards[state.form.page - 1].titleSize = selectedOptions[0].text;
form.value.json5.cards[form.value.page - 1].titleSize =
selectedOptions[0].text;
};
const onDescSizeConfirm = ({ selectedOptions }) => {
showDescSizePicker.value = false;
state.form.json5.cards[state.form.page - 1].descSize = selectedOptions[0].text;
form.value.json5.cards[form.value.page - 1].descSize =
selectedOptions[0].text;
};
const handleSubmit = async () => {
@ -288,56 +275,38 @@ export default defineComponent({
let res = await updateCusCard({
user_id: user_id,
card_title: state.form.title,
show_cus: state.form.showNfc,
cus_card: JSON.stringify(state.form),
card_title: form.value.title,
show_cus: form.value.showNfc,
cus_card: JSON.stringify(form.value),
});
if (res.code === 200) {
store.commit("user/setCusCard", JSON.stringify(state.form));
store.commit("user/setCusCard", JSON.stringify(form.value));
showSuccessToast("建立成功");
} else {
showToast.fail("建立失敗");
}
router.push("/");
};
return {
...toRefs(state),
sizeColumns,
showPreview,
showMovie,
showMvPopup,
showTitleSizePicker,
showDescSizePicker,
flexRef,
crop,
addCard,
delCard,
moveCard,
addBtn,
addShareBtn,
delBtn,
moveBtn,
afterRead,
handlePreview,
handleDelete,
onTitleSizeConfirm,
onDescSizeConfirm,
handleSubmit,
};
},
});
</script>
<template>
<div class="edit page">
<van-form @submit="handleSubmit">
<van-nav-bar class="bg-skyBlue py-1" left-arrow @click-left="$router.push('/')">
<van-nav-bar
class="bg-skyBlue py-1"
left-arrow
@click-left="$router.push('/')"
>
<template #title>
<h5 class="text-white mb-1"><strong>商務卡片設定</strong></h5>
</template>
<template #left>
<h4><i class="fa-solid fa-angle-left text-white" :style="{opacity:0.5}"></i></h4>
<h4>
<i
class="fa-solid fa-angle-left text-white"
:style="{ opacity: 0.5 }"
></i>
</h4>
</template>
</van-nav-bar>
@ -349,16 +318,35 @@ export default defineComponent({
</template>
<template #value>
<!-- <van-button size="small" class="ml-1 btn-moBlue" icon="tv-o" hairline @click="$router.push('/card/video')"></van-button> -->
<van-button size="small" class="ml-1 btn-moBlue" icon="tv-o" hairline @click="showMovie = true">教學影片</van-button>
<van-button size="small" class="ml-1 btn-skyBlue" icon="eye-o" hairline @click="handlePreview"></van-button>
<van-button
size="small"
class="ml-1 btn-moBlue"
icon="tv-o"
hairline
@click="showMovie = true"
>教學影片</van-button
>
<van-button
size="small"
class="ml-1 btn-skyBlue"
icon="eye-o"
hairline
@click="handlePreview"
>預覽</van-button
>
</template>
</van-cell>
<van-popup v-model:show="showMovie" class="p-3" closeable>
<iframe width="375" height="215" src="https://www.youtube.com/embed/fjZsQ0Rh6yk" title="Utel電子名片教學"
<iframe
width="375"
height="215"
src="https://www.youtube.com/embed/fjZsQ0Rh6yk"
title="Utel電子名片教學"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen></iframe>
allowfullscreen
></iframe>
</van-popup>
<van-field
@ -370,7 +358,11 @@ export default defineComponent({
placeholder="請輸入您在名片切換時顯示的文字"
:rules="[{ required: true, message: '標題文字必填' }]"
/>
<van-field label="是否顯示於感應名片" class="longText" input-align="right">
<van-field
label="是否顯示於感應名片"
class="longText"
input-align="right"
>
<template #input>
<van-switch
v-model="form.showNfc"
@ -380,13 +372,11 @@ export default defineComponent({
/>
</template>
</van-field>
</van-cell-group>
<van-cell-group inset class="mt-3">
<keep-alive>
<div id="app">
<!-- tab -->
<div class="bg-lightPink px-2 pt-3">
<ul class="nav nav-tabs">
@ -418,15 +408,43 @@ export default defineComponent({
<!-- /tab -->
<div class="py-2">
<van-field label="調整卡片順序" :border="false" v-if="form.json5.cards.length > 1">
<van-field
label="調整卡片順序"
:border="false"
v-if="form.json5.cards.length > 1"
>
<template #button>
<van-button size="small" class="ml-1 btn-skyBlue" icon="arrow-left" @click="moveCard(0, form.page)">前移</van-button>
<van-button size="small" class="ml-1 btn-skyBlue" icon="arrow" @click="moveCard(1, form.page)">後移</van-button>
<van-button size="small" class="ml-1 btn-tomatoRed" icon="delete-o" @click="delCard(form.page)"></van-button>
<van-button
size="small"
class="ml-1 btn-skyBlue"
icon="arrow-left"
@click="moveCard(0, form.page)"
>前移</van-button
>
<van-button
size="small"
class="ml-1 btn-skyBlue"
icon="arrow"
@click="moveCard(1, form.page)"
>後移</van-button
>
<van-button
size="small"
class="ml-1 btn-tomatoRed"
icon="delete-o"
@click="delCard(form.page)"
></van-button>
</template>
</van-field>
<van-divider :style="{ color: '#345068', borderColor: '#345068', padding: '0 16px' }"><strong>卡片設定</strong></van-divider>
<van-divider
:style="{
color: '#345068',
borderColor: '#345068',
padding: '0 16px',
}"
><strong>卡片設定</strong></van-divider
>
<van-field
v-model="form.json5.cards[form.page - 1].ratio"
@ -448,12 +466,20 @@ export default defineComponent({
<template #button>
<input
v-model="form.json5.cards[form.page - 1].bgColor"
type="color" class="form-control-color"
type="color"
class="form-control-color"
/>
</template>
</van-field>
<van-divider :style="{ color: '#345068', borderColor: '#345068', padding: '0 16px' }"><strong>內容圖片設定</strong></van-divider>
<van-divider
:style="{
color: '#345068',
borderColor: '#345068',
padding: '0 16px',
}"
><strong>內容圖片設定</strong></van-divider
>
<van-field
v-model="form.json5.cards[form.page - 1].ratio"
@ -475,7 +501,9 @@ export default defineComponent({
:src="form.json5.cards[form.page - 1].image"
alt=""
/>
<p class="ml-3 px-2 py-1 text-white bg-skyBlue rounded-pill">
<p
class="ml-3 px-2 py-1 text-white bg-skyBlue rounded-pill"
>
<i class="fa-solid fa-arrow-left"></i> 點擊更換圖片
</p>
</div>
@ -487,7 +515,9 @@ export default defineComponent({
src="@/assets/images/upload.jpg"
/>
<p class="ml-3">請上傳圖片</p>
<p class="ml-3 px-2 py-1 text-white bg-skyBlue rounded-pill">
<p
class="ml-3 px-2 py-1 text-white bg-skyBlue rounded-pill"
>
請上傳圖片
</p>
</div>
@ -504,7 +534,14 @@ export default defineComponent({
placeholder="連結(需輸入完整網址,http://..,https://...)"
/> -->
<van-divider :style="{ color: '#345068', borderColor: '#345068', padding: '0 16px' }"><strong>卡片標題</strong></van-divider>
<van-divider
:style="{
color: '#345068',
borderColor: '#345068',
padding: '0 16px',
}"
><strong>卡片標題</strong></van-divider
>
<van-field
v-model="form.json5.cards[form.page - 1].title"
@ -525,7 +562,11 @@ export default defineComponent({
placeholder="請輸入標題文字的色碼,如:#333333"
@click="showTitleSizePicker = true"
/>
<van-popup v-model:show="showTitleSizePicker" round position="bottom">
<van-popup
v-model:show="showTitleSizePicker"
round
position="bottom"
>
<van-picker
:columns="sizeColumns"
@cancel="showTitleSizePicker = false"
@ -544,12 +585,20 @@ export default defineComponent({
<template #button>
<input
v-model="form.json5.cards[form.page - 1].titleColor"
type="color" class="form-control-color"
type="color"
class="form-control-color"
/>
</template>
</van-field>
<van-divider :style="{ color: '#345068', borderColor: '#345068', padding: '0 16px' }"><strong>卡片說明文字</strong></van-divider>
<van-divider
:style="{
color: '#345068',
borderColor: '#345068',
padding: '0 16px',
}"
><strong>卡片說明文字</strong></van-divider
>
<van-field
v-model="form.json5.cards[form.page - 1].desc"
@ -573,7 +622,11 @@ export default defineComponent({
placeholder="請輸入說明文字的色碼,如:#333333"
@click="showDescSizePicker = true"
/>
<van-popup v-model:show="showDescSizePicker" round position="bottom">
<van-popup
v-model:show="showDescSizePicker"
round
position="bottom"
>
<van-picker
:columns="sizeColumns"
@cancel="showDescSizePicker = false"
@ -591,34 +644,77 @@ export default defineComponent({
<template #button>
<input
v-model="form.json5.cards[form.page - 1].descColor"
type="color" class="form-control-color"
type="color"
class="form-control-color"
/>
</template>
</van-field>
</div>
<van-divider :style="{ color: '#345068', borderColor: '#345068', padding: '0 16px' }"><strong>按鈕連結設定</strong></van-divider>
<van-field
label="新增按鈕"
input-align="right"
:border="false"
<van-divider
:style="{
color: '#345068',
borderColor: '#345068',
padding: '0 16px',
}"
><strong>按鈕連結設定</strong></van-divider
>
<van-field label="新增按鈕" input-align="right" :border="false">
<template #button>
<van-button size="small" class="ml-1 btn-outline-skyBlue" icon="add-o" @click="addBtn(form.page)"><h6>新增按鈕</h6></van-button>
<van-button size="small" class="ml-1 btn-outline-skyBlue" icon="share-o" @click="addShareBtn(form.page)"><h6>新增分享按鈕</h6></van-button>
<van-button
size="small"
class="ml-1 btn-outline-skyBlue"
icon="add-o"
@click="addBtn(form.page)"
><h6>新增按鈕</h6></van-button
>
<van-button
size="small"
class="ml-1 btn-outline-skyBlue"
icon="share-o"
@click="addShareBtn(form.page)"
><h6>新增分享按鈕</h6></van-button
>
</template>
</van-field>
<div class="p-3">
<div class="card shadow-sm" v-for="(btn, index) in form.json5.cards[form.page - 1].btns" :key="index">
<div class="card-header" :style="{backgroundColor:btn.color}">
<van-field :label="`控制按鈕 ${ index + 1 }`" :border="false" class="bg-transparent">
<div
class="card shadow-sm"
v-for="(btn, index) in form.json5.cards[form.page - 1].btns"
:key="index"
>
<div
class="card-header"
:style="{ backgroundColor: btn.color }"
>
<van-field
:label="`控制按鈕 ${index + 1}`"
:border="false"
class="bg-transparent"
>
<template #button>
<van-button size="small" class="bg-transparent border-0" icon="arrow-up" @click="moveBtn(0, index)" v-if="form.json5.cards[form.page - 1].btns.length > 1"></van-button>
<van-button size="small" class="bg-transparent border-0" icon="arrow-down" @click="moveBtn(1, index)" v-if="form.json5.cards[form.page - 1].btns.length > 1"></van-button>
<van-button size="small" class="bg-transparent border-0" icon="delete-o" @click="delBtn(index)"></van-button>
<van-button
size="small"
class="bg-transparent border-0"
icon="arrow-up"
@click="moveBtn(0, index)"
v-if="form.json5.cards[form.page - 1].btns.length > 1"
></van-button>
<van-button
size="small"
class="bg-transparent border-0"
icon="arrow-down"
@click="moveBtn(1, index)"
v-if="form.json5.cards[form.page - 1].btns.length > 1"
></van-button>
<van-button
size="small"
class="bg-transparent border-0"
icon="delete-o"
@click="delBtn(index)"
></van-button>
</template>
</van-field>
</div>
@ -653,7 +749,8 @@ export default defineComponent({
<template #button>
<input
v-model="btn.color"
type="color" class="form-control-color"
type="color"
class="form-control-color"
/>
</template>
</van-field>
@ -668,15 +765,22 @@ export default defineComponent({
:rules="[{ required: true, message: '標題文字色碼必填' }]"
>
<template #input>
<van-radio-group direction="horizontal" v-model="btn.btnHeight" checked-color="#345068">
<van-radio name="sm" @click="btn.btnHeight='sm'">sm</van-radio>
<van-radio name="md" @click="btn.btnHeight='md'">md</van-radio>
<van-radio-group
direction="horizontal"
v-model="btn.btnHeight"
checked-color="#345068"
>
<van-radio name="sm" @click="btn.btnHeight = 'sm'"
>sm</van-radio
>
<van-radio name="md" @click="btn.btnHeight = 'md'"
>md</van-radio
>
</van-radio-group>
</template>
</van-field>
</div>
</div>
</div>
<div class="px-5 pb-4">
@ -690,11 +794,11 @@ export default defineComponent({
</div>
</van-form>
</div>
</template>
<style lang="less" scoped>
.form-control-color,.form-control-color:valid {
.form-control-color,
.form-control-color:valid {
appearance: none;
background: 0 0;
border: 1px solid #ced4da;

@ -1,52 +0,0 @@
<template>
<van-nav-bar title="預覽" right-text="" @click-right="$router.push('/card/edit')" />
<div class="flex-section">
<div class="table-responsive">
<div class="chatbox">
<div id="flex" ref="flexRef"></div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onActivated, onDeactivated } from 'vue'
import { useRoute } from 'vue-router'
import { genCard1 } from '@/utils/card2'
const route = useRoute()
const flexRef = ref(null)
onActivated(() => {
flexRef.value.innerHTML = ''
let flexContent = genCard1(JSON.parse(route.params.content))
flex2html("flex", flexContent)
})
onDeactivated(() => {
})
</script>
<style lang="less" scoped>
.flex-section {
// color: white;
// z-index: 99999;
background-color: #666;
}
.chatbox {
background-color: #666;
margin-top: 10px;
padding-top: 10px;
}
.table-responsive {
width: 100%;
overflow-x: auto;
}
</style>

@ -0,0 +1,169 @@
<script setup>
import { ref, onMounted } from 'vue';
import { searchConnection,addUserFavi } from "@/api";
const tab2list = ref([]);
const tab2Loading = ref(false);
const tab2Finished = ref(false);
const tab2TodoLists = ref([]);
const tab2AddFriend = ref([]);
const loading = ref(false);
const finished = ref(false);
const form = ref({
user_id: 1,
real_name: '王小明',
avatar: '/src/assets/images/tp/tp_1.jpg',
note: '我是王小明',
supply: '我能分享的資源',
demand: '我需要的資源',
});
onMounted(async () => {
let res = await searchConnection();
console.log(res);
if(res.code===200){
tab2list.value = res.data;
}
res = await addUserFavi({uf_user_id:form.value.user_id});
// console.log(res);
if(res.code===200){
tab2AddFriend.value = res.data;
}
});
const tab2result = ref('');
const tab2showPicker = ref(false);
const tab2columns = [
{ text: '臺北', value: 'Taipei' },
{ text: '桃園', value: 'Taoyuan' },
{ text: '臺中', value: 'Taichung' },
{ text: '彰化', value: 'Changhua' },
{ text: '高雄', value: 'Kaohsiung' },
];
const tab2ListonLoad = () => {
//
// setTimeout ajax
// setTimeout(() => {
// for (let i = 0; i < 10; i++) {
// tab2list.value.push(tab2list.value.length + 1);
// }
// //
// tab2Loading.value = false;
// //
// if (tab2list.value.length >= 40) {
// tab2Finished.value = true;
// }
// }, 1000);
};
const tab2OnConfirm = ({ selectedOptions }) => {
tab2result.value = selectedOptions[0]?.text;
tab2showPicker.value = false;
};
const tab2searchValue = ref('');
const tab2OnSearch = () => {
// tab2TodoLists = tab2list.filter(tab2searchValue);
// console.log(tab2TodoLists)
};
</script>
<template>
<div class="content cnt2">
<van-cell-group inset>
<van-row align="center">
<van-col span="8">
<van-field
v-model="tab2result"
is-link
readonly
name="picker"
:border="false"
placeholder="縣市"
@click="tab2showPicker = true"
/>
<van-popup v-model:show="tab2showPicker" position="bottom">
<van-picker
:columns="tab2columns"
@confirm="tab2OnConfirm"
@cancel="tab2showPicker = false"
/>
</van-popup>
</van-col>
<van-col span="16">
<van-search
v-model="tab2searchValue"
placeholder="請輸入搜尋關鍵字"
@search="tab2OnSearch"
/>
</van-col>
</van-row>
<van-list
v-model:loading="loading"
:finished="finished"
finished-text="沒有更多了"
@load="tab2ListonLoad"
class="accordion"
id="accordion"
>
<!-- <van-cell v-for="item in list" :key="item" :title="item" > -->
<!-- <van-cell v-for="item in tab2list" :key="item" :title="item">
<template #title>
<div class="list-item">
<div class="avatar"><img :src="item.avatar"></div>
<div class="text">
<h5 class="name ellipsis">{{item.real_name}}</h5>
<div class="desc ellipsis">@{{item.note}}</div>
</div>
</div>
</template>
<template #value>
<van-button size="normal" class="border-0"><h5><i class="fa-solid fa-layer-group text-darkBlue"></i></h5></van-button>
<van-button size="normal" class="border-0"><h5><i class="fa-solid fa-comment-medical text-darkBlue"></i></h5></van-button>
</template>
</van-cell> -->
<div class="list-item" v-for="(item, index) in tab2list" :key="index">
<div class="left">
<div class="avatar"><img :src="item.avatar"></div>
<div class="text">
<h5 class="name ellipsis">{{item.real_name}}</h5>
<div class="desc ellipsis">@{{item.note}}</div>
</div>
</div>
<div class="right">
<van-button size="normal" class="border-0" data-toggle="collapse" :href="`#c${index}`" role="button" aria-expanded="false" :aria-controls="`c${index}`">
<h5><i class="fa-solid fa-layer-group text-darkBlue"></i></h5>
</van-button>
<van-button size="normal" class="border-0"><h5><i class="fa-solid fa-comment-medical text-darkBlue"></i></h5></van-button>
</div>
<div class="bottom collapse" :id="`c${index}`" data-parent="#accordion">
<h6 class="text-darkBlue"><strong>我能分享的資源</strong></h6>
<div class="desc">{{item.supply}}</div>
<div class="url py-2">網址<a href=""></a></div>
<hr/>
<h6 class="text-darkBlue"><strong>我需要的資源</strong></h6>
<div class="desc">{{item.demand}}</div>
</div>
</div>
</van-list>
</van-cell-group>
</div>
</template>
<style scoped>
</style>

@ -0,0 +1,196 @@
<script setup>
import { ref, onMounted } from "vue";
import { getUserInfo, updateCard } from "@/api";
const form = ref({ addon: [] });
// if (userRes.data.nfc_addon && (userRes.data.nfc_addon.length > 0)) {
// form.value.addon = JSON.parse(userRes.data.nfc_addon)
// }
onMounted(async () => {
let userRes = await getUserInfo();
if (userRes.code === 200) {
form.value = userRes.data;
}
});
const onAddBtn = () => {
if (form.value.addon) {
form.value.addon.push({ icon: "", name: "", link: "" });
} else {
form.value.addon = [{ icon: "", name: "", link: "" }];
}
};
const onDelBtn = (index) => {
form.value.addon.splice(index, 1);
};
const onMoveBtn = (type, index) => {
if (type === 0) {
if (index !== 0) {
[form.value.addon[index], form.value.addon[index - 1]] = [
form.value.addon[index - 1],
form.value.addon[index],
];
}
} else {
if (index + 1 !== form.value.addon.length) {
[form.value.addon[index + 1], form.value.addon[index]] = [
form.value.addon[index],
form.value.addon[index + 1],
];
}
}
};
const onSubmit = () => {
console.log(form.value);
};
</script>
<template>
<div class="content cnt1">
<van-cell-group inset>
<van-form @submit="onSubmit">
<van-field
v-model="form.nfcurl"
label="個人網頁"
name=""
placeholder="請輸入您的個人網頁"
/>
<van-field
v-model="form.line"
label="Line"
name=""
placeholder="請輸入您的Line ID"
/>
<van-field
v-model="form.facebook"
label="Facebook"
name=""
placeholder="請輸入您的Facebook"
/>
<van-field
v-model="form.ig"
label="IG"
name=""
placeholder="請輸入您的IG"
/>
<van-field
v-model="form.youTube"
label="YouTube"
name=""
placeholder="請輸入您的YouTube"
/>
<van-field
v-model="form.wechat"
label="WeChat"
name=""
placeholder="請輸入您的WeChat"
/>
<div class="px-5 py-4">
<van-button block class="btn-darkBlue" native-type="submit">
<h6>確認修改</h6>
</van-button>
</div>
</van-form>
</van-cell-group>
<van-cell-group inset>
<van-form @submit="onSubmit">
<van-cell class="bg-lightPink">
<template #title>
<h6 class="text-darkBlue"><strong>相關連結</strong></h6>
</template>
<template #value>
<van-button
size="small"
class="btn-outline-darkBlue"
@click="onAddBtn"
>
<h6>新增連結</h6>
</van-button>
</template>
</van-cell>
<van-field v-for="(item, idx) in form.addon" :key="idx">
<template #label>
<van-field
v-model="item.name"
name=""
maxlength="10"
placeholder="請輸入您的連結名稱"
:rules="[{ required: true, message: '連結名稱必填' }]"
/>
</template>
<template #input>
<van-field
v-model="item.link"
name=""
placeholder="請輸入您的按鈕連結"
:rules="[{ required: true, message: '按鈕連結必填' }]"
/>
</template>
<template #button>
<van-button
size="small"
class="ml-1 btn-tomatoRed"
icon="delete-o"
hairline
@click="onDelBtn(idx)"
></van-button>
<van-button
size="small"
class="ml-1 btn-skyBlue"
icon="arrow-up"
hairline
@click="onMoveBtn(0, idx)"
></van-button>
<van-button
size="small"
class="ml-1 btn-skyBlue"
icon="arrow-down"
plain
hairline
@click="onMoveBtn(1, idx)"
></van-button>
</template>
</van-field>
<!-- <van-swipe-cell v-for="(item,idx) in form.addon" :key="idx">
<template #left>
<van-button type="danger" icon="delete-o" plain hairline @click="onDelBtn(idx)"></van-button>
</template>
<van-field
v-model="item.name"
name=""
placeholder="請輸入您的連結名稱"
:rules="[{ required: true, message: '連結名稱必填' }]"
/>
<van-field
v-model="item.link"
name=""
placeholder="請輸入您的按鈕連結"
:rules="[{ pattern: /^([\w\.\-]){1,64}\@([\w\.\-]){1,64}/ , message: 'Email格式錯誤' }]"
/>
<template #right>
<van-button type="primary" icon="arrow-up" plain hairline @click="onMoveBtn(0,idx)"></van-button>
<van-button type="primary" icon="arrow-down" plain hairline @click="onMoveBtn(1,idx)"></van-button>
</template>
</van-swipe-cell> -->
<div class="px-5 py-4">
<van-button block class="btn-darkBlue" native-type="submit">
<h6>確認修改</h6>
</van-button>
</div>
</van-form>
</van-cell-group>
</div>
</template>
<style scoped></style>

@ -0,0 +1,70 @@
<script setup>
import { ref, onMounted } from 'vue';
import { setUserNfcTpl } from "@/api";
const tab3list = ref([]);
onMounted(async () => {
let res = await setUserNfcTpl();
console.log(res);
if(res.code===200){
tab3list.value = res.data;
}
});
const onSubmit = () => {
}
</script>
<template>
<div class="content cnt3">
<van-cell-group inset>
<van-form @submit="onSubmit">
<van-cell class="text-center bg-lightPink py-3">
<template #title>
<h6 class="text-darkBlue"><strong>感應式版型切換</strong></h6>
</template>
</van-cell>
<div class="text-center bg-dark px-5 pt-3">
<van-image
width="100%"
height="500"
position="top"
fit="cover"
src="/src/assets/images/tp/tp_1.jpg"
/>
</div>
<div class="d-flex justify-content-center py-3">
<van-button class="btn-tomatoRed px-5 mr-2">
預覽
</van-button>
<van-button class="btn-darkBlue px-5" native-type="submit">
確認修改
</van-button>
</div>
</van-form>
</van-cell-group>
<van-cell-group inset>
<van-row>
<van-col span="12" v-for="(item, index) in tab3list" :key="index">
<div class="imgBtn">
<div class="imgCnt">{{ item }}
<van-image
src="/src/assets/images/tp/tp_1.jpg"
/>
</div>
</div>
</van-col>
</van-row>
</van-cell-group>
</div>
</template>
<style scoped>
</style>

@ -0,0 +1,434 @@
<script setup>
import { ref, onMounted } from "vue";
import { Cropper } from "vue-advanced-cropper";
import "vue-advanced-cropper/dist/style.css";
import { getUserInfo, updateCard } from "@/api";
const crop = ref({
show: false,
img: null,
});
const fileList = ref([]);
const myCrop = ref(null);
const genderChecked = ref();
const showPicker = ref(false);
const showWorkPicker = ref(false);
const columns = [
{ text: "杭州", value: "Hangzhou" },
{ text: "宁波", value: "Ningbo" },
{ text: "温州", value: "Wenzhou" },
{ text: "绍兴", value: "Shaoxing" },
{ text: "湖州", value: "Huzhou" },
];
const columns2 = [
{ text: "杭州", value: "Hangzhou" },
{ text: "宁波", value: "Ningbo" },
{ text: "温州", value: "Wenzhou" },
{ text: "绍兴", value: "Shaoxing" },
{ text: "湖州", value: "Huzhou" },
];
const form = ref({ addon: [] });
onMounted(async () => {
let userRes = await getUserInfo();
if (userRes.code === 200) {
form.value = userRes.data;
}
});
const validatorUrl = (val) => {
if (val.length > 0) {
return /(https?:\/\/|line:\/\/|tel:|mailto:)\S+/.test(val);
} else {
return true;
}
};
const validatorTel = (val) => {
if (val.length > 0) {
return /(\d{2,3}-?|\(\d{2,3}\))\d{3,4}-?\d{4}/.test(val);
} else {
return true;
}
};
const afterRead = async (file, name) => {
crop.value.show = true;
const ofile = file.file;
crop.value.img = URL.createObjectURL(ofile);
// crop.value.img = ofile
return;
};
const onCrop = () => {
const { canvas } = myCrop.value.getResult();
if (canvas) {
const imgFile = new FormData();
canvas.toBlob(async (blob) => {
let ufile = new File([blob], "image.jpg");
imgFile.append("user_id", form.value.user_id);
imgFile.append("fileType", "IMAGE");
imgFile.append("file", ufile);
crop.value.show = false;
showLoadingToast({
duration: 0,
message: "圖片上傳中...",
forbidClick: true,
});
let res = await axios.post(
`${import.meta.env.VITE_APP_API_URL}/user/uploadAvatar`,
imgFile,
{}
);
if (res.data.code == 200) {
form.value.avatar = res.data.data;
showSuccessToast("上傳成功");
} else {
showToast.fail("上傳失敗");
}
}, "image/jpeg");
}
return;
};
const searchChecked = ref(true);
const searchOnUpdateValue = (newValue) => {
showConfirmDialog({
title: "提醒",
message: "是否切換開關?",
}).then(() => {
searchChecked.value = newValue;
});
};
const onSubmit = async () => {
console.log(form.value);
return;
showLoadingToast({
duration: 0,
message: "資料更新中...",
forbidClick: true,
});
let res = await updateCard(form.value);
if (res.code === 200) {
showSuccessToast("更新成功");
store.commit("user/setUserInfo", form.value);
router.push("/member");
} else {
showToast.fail("更新失敗");
}
};
const activeCollapse = ref(1);
</script>
<template>
<div class="content cnt0">
<van-cell-group inset>
<van-form @submit="onSubmit">
<div class="text-center p-4">
<van-uploader
:after-read="afterRead"
:max-count="1"
name="averter"
class="mb-4"
>
<div class="upload-main">
<img
class="upload-img"
:src="form.avatar"
alt=""
v-if="form.avatar"
/>
<img
class="upload-img"
src="@/assets/images/upload.jpg"
alt=""
v-else
/>
<div class="ml-3">
<p class="text-skyBlue mb-2">上傳圖片預設為Line頭像</p>
<van-button class="btn-darkBlue" icon="plus" block size="small"
>上傳檔案</van-button
>
</div>
</div>
</van-uploader>
<van-field
v-model="form.mark"
rows="3"
type="textarea"
maxlength="100"
placeholder="請輸入100字以內的簡介"
show-word-limit
:style="{ border: '1px #e3e3e3 solid', borderRadius: '6px' }"
/>
</div>
<van-field
v-model="form.real_name"
label="姓名"
name="pattern"
placeholder="請輸入您的姓名"
:rules="[{ required: true, message: '姓名為必填' }]"
/>
<van-field
v-model="form.company"
label="公司名稱"
name=""
placeholder="請輸入您的公司名稱"
:rules="[{ required: true, message: '公司名稱必填' }]"
/>
<van-field
v-model="form.title"
label="職稱"
name=""
placeholder="請輸入您的職稱"
/>
<van-field
v-model="form.phone"
label="手機"
name=""
type="tel"
placeholder="Ex. 0900000001 不要有空格"
:rules="[
{ required: true, message: '手機號必填' },
{ pattern: /\d{10}/, message: '手機號格式錯誤' },
]"
/>
<van-field
v-model="form.tel"
label="市話"
name=""
type="tel"
placeholder="請輸入您的市話"
:rules="[
{
validator: validatorTel,
message: '市話格式不正確,Ex. 02xxxx or 02-xxxx',
},
]"
/>
<van-field
v-model="form.email"
label="Email"
name=""
placeholder="請輸入您的Email"
:rules="[
{ required: true, message: 'Email必填' },
{
pattern: /^([\w\.\-]){1,64}\@([\w\.\-]){1,64}/,
message: 'Email格式錯誤',
},
]"
/>
<van-field
v-model="form.address"
label="住址"
name=""
placeholder="請輸入您的地址"
/>
<van-field
v-model="form.age"
label="年齡"
name=""
placeholder="請輸入您的年齡"
/>
<van-field name="radio" label="性別">
<template #input>
<van-radio-group
v-model="form.sex"
direction="horizontal"
checked-color="#345068"
>
<van-radio :name="0"></van-radio>
<van-radio :name="1"></van-radio>
<van-radio :name="2">未知</van-radio>
</van-radio-group>
</template>
</van-field>
<van-field
v-model="form.area"
is-link
readonly
name="picker"
label="區域"
placeholder="請選擇縣市區域"
@click="showPicker = true"
/>
<van-popup v-model:show="showPicker" position="bottom">
<van-picker
:columns="columns"
@confirm="onConfirm"
@cancel="showPicker = false"
/>
</van-popup>
<van-field
v-model="form.area"
is-link
readonly
name="picker2"
label="工作性質"
placeholder="請選擇工作性質"
@click="showWorkPicker = true"
/>
<van-popup v-model:show="showWorkPicker" position="bottom">
<van-picker
:columns="columns2"
@confirm="onConfirm"
@cancel="showWorkPicker = false"
/>
</van-popup>
<van-field readonly>
<template #label>是否公開<br />資料搜尋</template>
<template #input>
<van-switch
v-model="searchChecked"
@update:model-value="searchOnUpdateValue"
size="18px"
active-color="#345068"
inactive-color="#888888"
/>
</template>
</van-field>
<div class="p-5">
<van-button block class="btn-darkBlue" native-type="submit">
確認修改
</van-button>
</div>
</van-form>
</van-cell-group>
<van-cell-group inset>
<van-form @submit="onSubmit">
<van-cell class="text-center bg-lightPink py-3">
<template #title>
<h6 class="text-darkBlue"><strong>人脈資訊</strong></h6>
</template>
</van-cell>
<van-field
v-model="form.note"
name="pattern"
placeholder="請輸入簡短的自我介紹"
label-align="top"
>
<template #label>
<i class="fa-regular fa-gem text-darkBlue"></i> 一句話介紹自己
</template>
</van-field>
<van-collapse accordion v-model="activeCollapse">
<van-collapse-item name="1">
<template #title>
<i class="fa-regular fa-gem text-darkBlue"></i> 我能分享的資源
</template>
<van-field
v-model="form.supply"
rows="3"
type="textarea"
maxlength="100"
placeholder="請填寫能分享的資源"
show-word-limit
/>
<van-field
v-model="form.supply_link"
name="pattern"
placeholder="分享網址"
:rules="[
{
validator: validatorUrl,
message: '網址格式不正確,Ex. http://',
},
]"
/>
</van-collapse-item>
<van-collapse-item name="2">
<template #title>
<i class="fa-regular fa-gem text-darkBlue"></i> 我需要的資源
</template>
<van-field
v-model="form.demand"
rows="3"
type="textarea"
maxlength="100"
placeholder="請填寫能分享的資源"
show-word-limit
/>
<van-field
v-model="form.demand_link"
name="pattern"
placeholder="需求連結"
/>
</van-collapse-item>
<van-collapse-item name="3">
<template #title>
<i class="fa-regular fa-gem text-darkBlue"></i> 菁英交流資訊
</template>
<van-field
v-model="form.exchange"
rows="3"
type="textarea"
maxlength="100"
placeholder="請填寫能分享的資源"
show-word-limit
/>
<van-field
v-model="form.exchange_link"
name="pattern"
placeholder="需求連結"
/>
</van-collapse-item>
</van-collapse>
<div class="p-5">
<van-button block class="btn-darkBlue" native-type="submit">
確認修改
</van-button>
</div>
</van-form>
</van-cell-group>
</div>
<van-popup class="cropPopup" v-model:show="crop.show" closeable>
<h5 class="text-center mt-3">檔案裁切上傳</h5>
<div class="cropper-section">
<div class="crop-area">
<cropper
class="cropper"
ref="myCrop"
:src="crop.img"
:stencil-props="{
aspectRatio: 1 / 1,
}"
:auto-zoom="true"
/>
</div>
<van-button
round
class="btn-tomatoRed rounded-pill w-50 mt-3"
size="small"
@click="onCrop"
>
裁切確認
</van-button>
</div>
</van-popup>
</template>
<style lang="less" scoped></style>
Loading…
Cancel
Save