flat: 添加微信分享卡片

This commit is contained in:
史典卓
2025-07-14 15:38:39 +08:00
parent 645c2552f6
commit ec2dc5f659
22 changed files with 232 additions and 106 deletions

BIN
.DS_Store vendored

Binary file not shown.

28
App.vue
View File

@@ -3,7 +3,9 @@ import { reactive, inject, onMounted } from 'vue';
import { onLaunch, onShow, onHide } from '@dcloudio/uni-app';
import useUserStore from './stores/useUserStore';
import useDictStore from './stores/useDictStore';
import { setupWechatShare, generateShareLink } from '@/utils/wechatShare.js';
const { $api, navTo, appendScriptTagElement } = inject('globalFunction');
import config from '@/config.js';
onLaunch((options) => {
useDictStore().getDictData();
@@ -26,15 +28,10 @@ onLaunch((options) => {
onMounted(() => {
// #ifndef MP-WEIXIN
if (process.env.NODE_ENV === 'development') {
appendScriptTagElement('./static/js/jweixin-1.4.0.js').then(() => {
appendScriptTagElement('https://qd.zhaopinzao8dian.com/file/csn/jweixin-1.4.0.js').then(() => {
console.log('✅ 微信 JSSDK 加载完成');
signatureFn();
});
} else {
appendScriptTagElement('/static/js/jweixin-1.4.0.js').then(() => {
console.log('✅ 微信 JSSDK 加载完成');
});
}
// #endif
});
@@ -45,6 +42,17 @@ onShow(() => {
onHide(() => {
console.log('App Hide');
});
function signatureFn() {
const link = generateShareLink();
// console.log('首页link', link);
setupWechatShare({
title: config.shareConfig.title,
desc: config.shareConfig.desc,
link: link,
imgUrl: config.shareConfig.imgUrl,
});
}
</script>
<style>
@@ -81,19 +89,19 @@ uni-modal,
@font-face {
font-family: PingFangSC-Regular;
src: url('/static/font/PingFangSC-Regular.woff2') format('woff2');
src: url('https://qd.zhaopinzao8dian.com/file/csn/PingFangSC-Regular.woff2') format('woff2');
font-display: swap;
}
@font-face {
font-family: PingFangSC-Medium;
src: url('/static/font/PingFangSC-Medium.woff2') format('woff2');
src: url('https://qd.zhaopinzao8dian.com/file/csn/PingFangSC-Medium.woff2') format('woff2');
font-display: swap;
}
@font-face {
font-family: DIN-Medium;
src: url('/static/font/DIN-Medium.woff2') format('woff2');
src: url('https://qd.zhaopinzao8dian.com/file/csn/DIN-Medium.woff2') format('woff2');
font-display: swap;
}

View File

@@ -543,6 +543,7 @@ function isEmptyObject(obj) {
return obj && typeof obj === 'object' && !Array.isArray(obj) && Object.keys(obj).length === 0;
}
export const $api = {
msg,
prePage,
@@ -584,5 +585,5 @@ export default {
appendScriptTagElement,
insertSortData,
isInWechatMiniProgramWebview,
isEmptyObject
isEmptyObject,
}

View File

@@ -63,5 +63,11 @@ export default {
experience: 0.3, //经验
salary: 0.5, // 薪资
areas: 0.5 // 区域
},
shareConfig: {
baseUrl: 'https://qd.zhaopinzao8dian.com',
title: '找工作,用 AI 更高效|青岛市智能求职平台',
desc: '融合海量岗位、智能简历匹配、竞争力分析,助你精准锁定理想职位!',
imgUrl: 'https://qd.zhaopinzao8dian.com/file/csn/qd_shareLogo.jpg',
}
}

View File

@@ -24,7 +24,7 @@
vConsole.destroy();
</script> -->
</head>
<body>
<!-- <body> -->
<div id="app"><!--app-html--></div>
<script type="module" src="/main.js"></script>
</body>

View File

@@ -1,7 +1,7 @@
<template>
<AppLayout title="" :use-scroll-view="false">
<template #headerleft>
<view class="btn">
<view class="btnback">
<image src="@/static/icon/back.png" @click="navBack"></image>
</view>
</template>
@@ -143,12 +143,16 @@ function expand() {
</script>
<style lang="stylus" scoped>
.btnback{
width: 64rpx;
height: 64rpx;
}
.btn {
display: flex;
justify-content: space-between;
align-items: center;
width: 60rpx;
height: 60rpx;
width: 52rpx;
height: 52rpx;
}
image {
height: 100%;

View File

@@ -1,7 +1,7 @@
<template>
<AppLayout title="我的浏览" :show-bg-image="false" :use-scroll-view="false">
<template #headerleft>
<view class="btn">
<view class="btnback">
<image src="@/static/icon/back.png" @click="navBack"></image>
</view>
</template>
@@ -152,12 +152,16 @@ function getPreviousDay(dateStr) {
</script>
<style lang="stylus" scoped>
.btnback{
width: 64rpx;
height: 64rpx;
}
.btn {
display: flex;
justify-content: space-between;
align-items: center;
width: 60rpx;
height: 60rpx;
width: 52rpx;
height: 52rpx;
}
image {
height: 100%;

View File

@@ -1,7 +1,7 @@
<template>
<AppLayout :title="title" :show-bg-image="false" @onScrollBottom="getDataList('add')">
<template #headerleft>
<view class="btn">
<view class="btnback">
<image src="@/static/icon/back.png" @click="navBack"></image>
</view>
</template>
@@ -101,12 +101,16 @@ function getDataList(type = 'add') {
</script>
<style lang="stylus" scoped>
.btnback{
width: 64rpx;
height: 64rpx;
}
.btn {
display: flex;
justify-content: space-between;
align-items: center;
width: 60rpx;
height: 60rpx;
width: 52rpx;
height: 52rpx;
}
image {
height: 100%;

View File

@@ -1,7 +1,7 @@
<template>
<AppLayout title="" :use-scroll-view="false">
<template #headerleft>
<view class="btn">
<view class="btnback">
<image src="@/static/icon/back.png" @click="navBack"></image>
</view>
</template>
@@ -219,12 +219,16 @@ function getHoursBetween(startTimeStr, endTimeStr) {
</script>
<style lang="stylus" scoped>
.btnback{
width: 64rpx;
height: 64rpx;
}
.btn {
display: flex;
justify-content: space-between;
align-items: center;
width: 60rpx;
height: 60rpx;
width: 52rpx;
height: 52rpx;
}
image {
height: 100%;

View File

@@ -1,11 +1,14 @@
<template>
<AppLayout title="" backGorundColor="#F4F4F4">
<template #headerleft>
<view class="btn">
<view class="btnback">
<image src="@/static/icon/back.png" @click="navBack"></image>
</view>
</template>
<template #headerright>
<!-- <view class="btnshare">
<image src="@/static/icon/share.png" @click="shareJob"></image>
</view> -->
<view class="btn mar_ri10">
<image src="@/static/icon/collect3.png" v-if="!jobInfo.isCollection" @click="jobCollection"></image>
<image src="@/static/icon/collect2.png" v-else @click="jobCollection"></image>
@@ -137,10 +140,12 @@
import point from '@/static/icon/point.png';
import VideoPlayer from './component/videoPlayer.vue';
import { reactive, inject, watch, ref, onMounted, computed } from 'vue';
import { onLoad, onShow } from '@dcloudio/uni-app';
import { onLoad, onShow, onHide } from '@dcloudio/uni-app';
import dictLabel from '@/components/dict-Label/dict-Label.vue';
const { $api, navTo, getLenPx, parseQueryParams, navBack, isEmptyObject } = inject('globalFunction');
import RadarMap from './component/radarMap.vue';
import { updateWechatShare, generateShareLink } from '@/utils/wechatShare.js';
const { $api, navTo, getLenPx, parseQueryParams, navBack, isEmptyObject } = inject('globalFunction');
import config from '@/config.js';
const matchingDegree = ref(['一般', '良好', '优秀', '极好']);
const currentStep = ref(1);
const companyCount = ref(0);
@@ -165,11 +170,31 @@ onShow(() => {
}
});
onHide(() => {
const link = generateShareLink();
// console.log('首页link', link);
updateWechatShare({
title: config.shareConfig.title,
desc: config.shareConfig.desc,
link: link,
imgUrl: config.shareConfig.imgUrl,
});
});
function initLoad(option) {
const jobId = atob(option.jobId);
if (jobId !== jobIdRef.value) {
jobIdRef.value = jobId;
getDetail(jobId);
getDetail(jobId).then((data) => {
const link = generateShareLink(btoa(data.jobId));
// console.log('详情', link);
updateWechatShare({
title: '职位推荐:' + data.jobTitle,
desc: data.description,
link: link,
imgUrl: 'https://qd.zhaopinzao8dian.com/file/csn/qd_shareLogo.jpg',
});
});
}
}
@@ -182,9 +207,11 @@ function seeExplain() {
}
function getDetail(jobId) {
return new Promise((reslove, reject) => {
$api.createRequest(`/app/job/${jobId}`).then((resData) => {
const { latitude, longitude, companyName, companyId } = resData.data;
jobInfo.value = resData.data;
reslove(resData.data);
getCompanyIsAJobs(companyId);
getCompetivetuveness(jobId);
if (latitude && longitude) {
@@ -207,6 +234,7 @@ function getDetail(jobId) {
];
}
});
});
}
function getCompanyIsAJobs(companyId) {
@@ -278,12 +306,21 @@ function getClass(index) {
</script>
<style lang="stylus" scoped>
.btnback{
width: 64rpx;
height: 64rpx;
}
.btn {
display: flex;
justify-content: space-between;
align-items: center;
width: 60rpx;
height: 60rpx;
width: 52rpx;
height: 52rpx;
}
.btnshare {
width: 48rpx;
height: 48rpx;
margin-right: 46rpx;
}
image {
height: 100%;

View File

@@ -1,7 +1,7 @@
<template>
<AppLayout title="附近" :use-scroll-view="false">
<template #headerleft>
<view class="btn">
<view class="btnback">
<image src="@/static/icon/back.png" @click="navBack"></image>
</view>
</template>
@@ -83,12 +83,16 @@ function handleTabChange(index) {
</script>
<style lang="stylus" scoped>
.btnback{
width: 64rpx;
height: 64rpx;
}
.btn {
display: flex;
justify-content: space-between;
align-items: center;
width: 60rpx;
height: 60rpx;
width: 52rpx;
height: 52rpx;
}
image {
height: 100%;

BIN
static/.DS_Store vendored

Binary file not shown.

BIN
static/font/.DS_Store vendored

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
static/icon/share.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 B

36
static/share.html Normal file
View File

@@ -0,0 +1,36 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<title>找工作,用 AI 更高效|青岛市智能求职平台</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- 微信分享卡片标签(动态填充) -->
<meta property="og:title" content="找工作,用 AI 更高效|青岛市智能求职平台" />
<meta property="og:description" content="融合海量岗位、智能简历匹配、竞争力分析,助你精准锁定理想职位!" />
<meta property="og:image" content="https://qd.zhaopinzao8dian.com/file/csn/qd_shareLogo.jpg" />
<meta property="og:url" content="https://qd.zhaopinzao8dian.com" />
<meta name="description" content="融合海量岗位、智能简历匹配、竞争力分析,助你精准锁定理想职位!">
<script>
const params = new URLSearchParams(location.search)
const jobId = params.get('jobId')
// document.querySelector('meta[property="og:url"]').setAttribute('content', location.href)
// 延迟跳转到 Vue 页面
setTimeout(() => {
if (jobId) {
window.location.href = `/#/packageA/pages/post/post?jobId=${jobId}`
} else {
window.location.href = '/#/'
}
}, 300)
// 测试使用 分享等形式打开
// http://localhost:5173/app/static/share.html?jobId=MTE4MzQ4NTE4
// http://localhost:5173/app/static/share.html?jobId=MTE4MzQ4NTE4&_t=1752221704007#/
</script>
</head>
<body>
<p>正在加载中...</p>
</body>
</html>

BIN
unpackage/.DS_Store vendored

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,5 +1,7 @@
import wx from 'weixin-js-sdk'
import config from "@/config.js"
import {
$api
} from '../common/globalFunction';
export function setupWechatShare({
title,
@@ -8,14 +10,16 @@ export function setupWechatShare({
imgUrl
}) {
// 通过后端接口获取签名(必须)
fetch(`${config.baseUrl}/wechat-signature?url=${encodeURIComponent(location.href.split('#')[0])}`)
.then(res => res.json())
.then(({
$api.createRequest('/app/job/getWechatUrl', {
imgUrl: location.href.split('#')[0]
}, 'POST').then((resData) => {
const {
appId,
timestamp,
nonceStr,
signature
}) => {
} = resData.data
wx.config({
debug: false,
appId,
@@ -24,7 +28,6 @@ export function setupWechatShare({
signature,
jsApiList: ['updateAppMessageShareData', 'updateTimelineShareData']
})
wx.ready(() => {
// 分享给好友
wx.updateAppMessageShareData({
@@ -33,31 +36,46 @@ export function setupWechatShare({
link,
imgUrl,
success: () => {
console.log('分享配置成功')
$api.msg('分享配置成功')
}
})
// 分享到朋友圈
wx.updateTimelineShareData({
title,
link,
imgUrl,
success: () => {
console.log('朋友圈分享配置成功')
$api.msg('朋友圈分享配置成功')
}
})
})
}).catch((err) => {
$api.msg('获取微信签名失败')
console.error('获取微信签名失败:', err);
});
})
}
// 使用
// import { setupWechatShare } from '@/utils/wechatShare.js'
export function updateWechatShare({
title,
desc,
link,
imgUrl
}) {
if (!window.wx) return;
wx.updateAppMessageShareData({
title,
desc,
link,
imgUrl,
success: () => console.log('分享配置成功'),
fail: (err) => console.warn('分享配置失败', err)
});
}
// onMounted(() => {
// setupWechatShare({
// title: '职位推荐:高级前端工程师',
// desc: '某知名互联网公司年薪40W点击查看详情',
// link: location.href,
// imgUrl: 'https://yourcdn.com/job-thumbnail.png'
// })
// })
// tools
export function generateShareLink(jobId) {
const base = location.origin + '/app/static/share.html';
const query = jobId ? `?jobId=${jobId}&_t=${Date.now()}` : `?_t=${Date.now()}`;
return `${base}${query}`;
}