This commit is contained in:
gaofeng
2026-05-12 18:27:28 +08:00
commit 6d9aee81aa
3664 changed files with 274415 additions and 0 deletions

861
app/home/common.php Normal file
View File

@@ -0,0 +1,861 @@
<?php
const MODEL = 'mala';
const PAYMODEL = 'tyatjyzxmala';
const PAY_MARK = 'tyatjyzxmala';
const COUNTRY = '马来西亚';
const COUNTRY_EN = 'Mala ';
const BAIDUACCESSTOKENDIR = '/www/wwwroot/temcache/';
const GET_WEB_CONFIG_URL = 'https://uniuser.jzvisa.com/home/Company/index/model/';
const BUCKET = 'hzmala';
const SOURCE = 'evisa.hkpgsow.cn';
const OSS_URL = 'https://files.jzvisa.com/' . BUCKET . '/';
const PAY_TYPE = ['1' => 'wechatpay', '2' => 'alipay'];
const CONFIG_JSON_NATION_EN = './json/nation_en.json';
const CONFIG_JSON_PHONE_PREFIX_EN = './json/phone_prefix_en.json';
const CONFIG_JSON_TRAVEL_COUNTRY_EN = './json/travel_country_en.json';
const CONFIG_JSON_NATION = './json/nation.json';
const CONFIG_JSON_PHONE_PREFIX = './json/phone_prefix.json';
const CONFIG_JSON_TRAVEL_COUNTRY = './json/travel_country.json';
const CONFIG_JSON_STATE = './json/state.json';
const CONTACT_MODEL = 'contact';
const CONFIG_JSON_VISA_NATIONALITY = './json/visa_nationality.json';
const CONFIG_JSON_NATIONALITY_MAP = './json/visa_nationality_map.json';
const ARRIVE_DATE_COUNT = '2';
const BASE_PATH = '/mys'; // 可后端模板注入
const CONTACT_REASON = [
'我需要解决付款问题',
'我需要协助填写申请表格',
'我需要关于旅行证件的信息',
'我需要更正我的旅行证件或已提交申请的信息',
'我想检查已提交的申请的状态',
'我想申请退款',
'我有其他未列明的原因'
];
const CONTACT_REASON_EN = [
'I need to resolve a payment issue',
'I need assistance with filling out the application form',
'I need information about travel documents',
'I need to correct information on my travel document or submitted application',
'I want to check the status of my submitted application',
'I want to request a refund',
'I have other reasons not listed'
];
// 公共函数
function build_ocr_signature(string $appId, string $secret, ?int $timestamp = null): array
{
$timestamp = $timestamp ?? time();
$signString = $appId . "\n" . (string)$timestamp;
$signature = hash_hmac('sha256', $signString, $secret);
return [
'app_id' => $appId,
'timestamp' => (string)$timestamp,
'signature' => $signature,
'base_url' => config('app.OCR_BASE_URL'),
];
}
function isDateTodayTomorrowOrDayAfter($dateString, $country = '') {
// 将输入日期转换为 DateTime 对象
$inputDate = new DateTime($dateString);
$inputDate->setTime(0, 0, 0); // 忽略时间部分,只比较日期
// 获取今天的日期(午夜时间)
$today = new DateTime('today');
if ($inputDate < $today) {
return true;
}
// 获取明天和后天的日期
$tomorrow = new DateTime('tomorrow');
if ($country == 'thailand') {
$dayAfterTomorrow = new DateTime('tomorrow +1 day');
$dayAfterTomorrow1 = new DateTime('tomorrow +2 day');
return $inputDate == $today || $inputDate == $tomorrow || $inputDate == $dayAfterTomorrow || $inputDate == $dayAfterTomorrow1;
} else if ($country == 'indonesia') {
$yeasterday = new DateTime('yesterday');
$dayAfterTomorrow = new DateTime('tomorrow +1 day');
return $inputDate == $yeasterday || $inputDate == $today || $inputDate == $tomorrow || $inputDate == $dayAfterTomorrow;
} else {
$dayAfterTomorrow = new DateTime('tomorrow +1 day');
return $inputDate == $today || $inputDate == $tomorrow || $inputDate == $dayAfterTomorrow;
}
}
function find_json($json_name, $field, $field_value)
{
$data = file_get_contents($json_name);
$data_arr = json_decode($data, true);
$found = array_filter($data_arr, function ($item) use ($field, $field_value) {
return $item[$field] === $field_value;
});
// 输出结果
if ($found) {
$found = array_values($found); // 获取过滤后的第一个元素
return $found;
} else {
return false;
}
}
function getNameById($json_name, $id)
{
$data = file_get_contents($json_name);
$data_arr = json_decode($data, true);
foreach ($data_arr as $item) {
if (array_key_exists('data', $item['data'])) {
foreach ($item['data']['data'] as $dataItem) {
if ($dataItem['id'] === $id) {
return $dataItem['name'];
}
}
}
}
return null;
}
function getIdByName($json_name, $id)
{
$data = file_get_contents($json_name);
$data_arr = json_decode($data, true);
foreach ($data_arr as $item) {
if (array_key_exists('data', $item['data'])) {
foreach ($item['data']['data'] as $dataItem) {
if ($dataItem['name'] === $id) {
return $dataItem['id'];
}
}
}
}
return null;
}
function isMobile()
{
$_SERVER['ALL_HTTP'] = isset($_SERVER['ALL_HTTP']) ? $_SERVER['ALL_HTTP'] : '';
if (preg_match('/(up.browser|up.link|mmp|symbian|smartphone|midp|wap|phone|iphone|ipad|ipod|android|xoom)/i',
strtolower($_SERVER['HTTP_USER_AGENT']))) {
return true;
}
if ((isset($_SERVER['HTTP_ACCEPT'])) and (strpos(strtolower($_SERVER['HTTP_ACCEPT']),
'application/vnd.wap.xhtml+xml') !== false)) {
return true;
}
if (isset($_SERVER['HTTP_X_WAP_PROFILE'])) {
return true;
}
if (isset($_SERVER['HTTP_PROFILE'])) {
return true;
}
$mobile_ua = strtolower(substr($_SERVER['HTTP_USER_AGENT'], 0, 4));
$mobile_agents = array(
'w3c ',
'acs-',
'alav',
'alca',
'amoi',
'audi',
'avan',
'benq',
'bird',
'blac',
'blaz',
'brew',
'cell',
'cldc',
'cmd-',
'dang',
'doco',
'eric',
'hipt',
'inno',
'ipaq',
'java',
'jigs',
'kddi',
'keji',
'leno',
'lg-c',
'lg-d',
'lg-g',
'lge-',
'maui',
'maxo',
'midp',
'mits',
'mmef',
'mobi',
'mot-',
'moto',
'mwbp',
'nec-',
'newt',
'noki',
'oper',
'palm',
'pana',
'pant',
'phil',
'play',
'port',
'prox',
'qwap',
'sage',
'sams',
'sany',
'sch-',
'sec-',
'send',
'seri',
'sgh-',
'shar',
'sie-',
'siem',
'smal',
'smar',
'sony',
'sph-',
'symb',
't-mo',
'teli',
'tim-',
'tosh',
'tsm-',
'upg1',
'upsi',
'vk-v',
'voda',
'wap-',
'wapa',
'wapi',
'wapp',
'wapr',
'webc',
'winw',
'winw',
'xda',
'xda-'
);
if (in_array($mobile_ua, $mobile_agents)) {
return true;
}
if (strpos(strtolower($_SERVER['ALL_HTTP']), 'operamini') !== false) {
return true;
}
// Pre-final check to reset everything if the user is on Windows
if (strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'windows') !== false) {
return false;
}
// But WP7 is also Windows, with a slightly different characteristic
if (strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'windows phone') !== false) {
return true;
}
return false;
}
function getOrderNumber()
{
return '1003' . date('YmdHis') . mt_rand(10, 99);
}
function cmf_get_current_user_id()
{
$sessionUserId = session('user.id');
if (empty($sessionUserId)) {
return 0;
}
return $sessionUserId;
}
/**
* 身份证信息
* @param array $data
* @return void
*/
function parseIdentification(array $data)
{
$res = [
'national_identification_number' => '',
'provice' => '',
'city' => '',
'address' => ''
];
if (!is_array($data) || empty($data)) {
return $res;
}
$res['national_identification_number'] = $data['words_result']['公民身份号码']['words'] ?? '';
$address = paresAddress($data['words_result']['住址']['words'] ?? '');
$res['provice'] = $address['provice'];
$res['city'] = $address['city'];
$res['address'] = $address['address'];
return $res;
}
function getprovince($city)
{
if (empty($city)){
return '';
}
// 构建市与省份的对应关系数组
$cityProvinceMap = [
// 华北地区
"北京市" => "北京市",
"天津市" => "天津市",
"石家庄市" => "河北省",
"唐山市" => "河北省",
"秦皇岛市" => "河北省",
"邯郸市" => "河北省",
"邢台市" => "河北省",
"保定市" => "河北省",
"张家口市" => "河北省",
"承德市" => "河北省",
"沧州市" => "河北省",
"廊坊市" => "河北省",
"衡水市" => "河北省",
"太原市" => "山西省",
"大同市" => "山西省",
"阳泉市" => "山西省",
"长治市" => "山西省",
"晋城市" => "山西省",
"朔州市" => "山西省",
"晋中市" => "山西省",
"运城市" => "山西省",
"忻州市" => "山西省",
"临汾市" => "山西省",
"吕梁市" => "山西省",
"呼和浩特市" => "内蒙古自治区",
"包头市" => "内蒙古自治区",
"乌海市" => "内蒙古自治区",
"赤峰市" => "内蒙古自治区",
"通辽市" => "内蒙古自治区",
"鄂尔多斯市" => "内蒙古自治区",
"呼伦贝尔市" => "内蒙古自治区",
"巴彦淖尔市" => "内蒙古自治区",
"乌兰察布市" => "内蒙古自治区",
// 东北地区
"沈阳市" => "辽宁省",
"大连市" => "辽宁省",
"鞍山市" => "辽宁省",
"抚顺市" => "辽宁省",
"本溪市" => "辽宁省",
"丹东市" => "辽宁省",
"锦州市" => "辽宁省",
"营口市" => "辽宁省",
"阜新市" => "辽宁省",
"辽阳市" => "辽宁省",
"盘锦市" => "辽宁省",
"铁岭市" => "辽宁省",
"朝阳市" => "辽宁省",
"葫芦岛市" => "辽宁省",
"长春市" => "吉林省",
"吉林市" => "吉林省",
"四平市" => "吉林省",
"辽源市" => "吉林省",
"通化市" => "吉林省",
"白山市" => "吉林省",
"松原市" => "吉林省",
"白城市" => "吉林省",
"延边朝鲜族自治州" => "吉林省",
"哈尔滨市" => "黑龙江省",
"齐齐哈尔市" => "黑龙江省",
"鸡西市" => "黑龙江省",
"鹤岗市" => "黑龙江省",
"双鸭山市" => "黑龙江省",
"大庆市" => "黑龙江省",
"伊春市" => "黑龙江省",
"佳木斯市" => "黑龙江省",
"七台河市" => "黑龙江省",
"牡丹江市" => "黑龙江省",
"黑河市" => "黑龙江省",
"绥化市" => "黑龙江省",
"大兴安岭地区" => "黑龙江省",
// 华东地区
"上海市" => "上海市",
"南京市" => "江苏省",
"无锡市" => "江苏省",
"徐州市" => "江苏省",
"常州市" => "江苏省",
"苏州市" => "江苏省",
"南通市" => "江苏省",
"连云港市" => "江苏省",
"淮安市" => "江苏省",
"盐城市" => "江苏省",
"扬州市" => "江苏省",
"镇江市" => "江苏省",
"泰州市" => "江苏省",
"宿迁市" => "江苏省",
"杭州市" => "浙江省",
"宁波市" => "浙江省",
"温州市" => "浙江省",
"嘉兴市" => "浙江省",
"湖州市" => "浙江省",
"绍兴市" => "浙江省",
"金华市" => "浙江省",
"衢州市" => "浙江省",
"舟山市" => "浙江省",
"台州市" => "浙江省",
"丽水市" => "浙江省",
"合肥市" => "安徽省",
"芜湖市" => "安徽省",
"蚌埠市" => "安徽省",
"淮南市" => "安徽省",
"马鞍山市" => "安徽省",
"淮北市" => "安徽省",
"铜陵市" => "安徽省",
"安庆市" => "安徽省",
"黄山市" => "安徽省",
"滁州市" => "安徽省",
"阜阳市" => "安徽省",
"宿州市" => "安徽省",
"六安市" => "安徽省",
"亳州市" => "安徽省",
"池州市" => "安徽省",
"宣城市" => "安徽省",
"福州市" => "福建省",
"厦门市" => "福建省",
"莆田市" => "福建省",
"三明市" => "福建省",
"泉州市" => "福建省",
"漳州市" => "福建省",
"南平市" => "福建省",
"龙岩市" => "福建省",
"宁德市" => "福建省",
"南昌市" => "江西省",
"景德镇市" => "江西省",
"萍乡市" => "江西省",
"九江市" => "江西省",
"新余市" => "江西省",
"鹰潭市" => "江西省",
"赣州市" => "江西省",
"吉安市" => "江西省",
"宜春市" => "江西省",
"抚州市" => "江西省",
"上饶市" => "江西省",
"济南市" => "山东省",
"青岛市" => "山东省",
"淄博市" => "山东省",
"枣庄市" => "山东省",
"东营市" => "山东省",
"烟台市" => "山东省",
"潍坊市" => "山东省",
"济宁市" => "山东省",
"泰安市" => "山东省",
"威海市" => "山东省",
"日照市" => "山东省",
"临沂市" => "山东省",
"德州市" => "山东省",
"聊城市" => "山东省",
"滨州市" => "山东省",
"菏泽市" => "山东省",
// 中南地区
"郑州市" => "河南省",
"开封市" => "河南省",
"洛阳市" => "河南省",
"平顶山市" => "河南省",
"安阳市" => "河南省",
"鹤壁市" => "河南省",
"新乡市" => "河南省",
"焦作市" => "河南省",
"濮阳市" => "河南省",
"许昌市" => "河南省",
"漯河市" => "河南省",
"三门峡市" => "河南省",
"南阳市" => "河南省",
"商丘市" => "河南省",
"信阳市" => "河南省",
"周口市" => "河南省",
"驻马店市" => "河南省",
"武汉市" => "湖北省",
"黄石市" => "湖北省",
"十堰市" => "湖北省",
"宜昌市" => "湖北省",
"襄阳市" => "湖北省",
"鄂州市" => "湖北省",
"荆门市" => "湖北省",
"孝感市" => "湖北省",
"荆州市" => "湖北省",
"黄冈市" => "湖北省",
"咸宁市" => "湖北省",
"随州市" => "湖北省",
"恩施土家族苗族自治州" => "湖北省",
"长沙市" => "湖南省",
"株洲市" => "湖南省",
"湘潭市" => "湖南省",
"衡阳市" => "湖南省",
"邵阳市" => "湖南省",
"岳阳市" => "湖南省",
"常德市" => "湖南省",
"张家界市" => "湖南省",
"益阳市" => "湖南省",
"郴州市" => "湖南省",
"永州市" => "湖南省",
"怀化市" => "湖南省",
"娄底市" => "湖南省",
"湘西土家族苗族自治州" => "湖南省",
"广州市" => "广东省",
"韶关市" => "广东省",
"深圳市" => "广东省",
"珠海市" => "广东省",
"汕头市" => "广东省",
"佛山市" => "广东省",
"江门市" => "广东省",
"湛江市" => "广东省",
"茂名市" => "广东省",
"肇庆市" => "广东省",
"惠州市" => "广东省",
"梅州市" => "广东省",
"汕尾市" => "广东省",
"河源市" => "广东省",
"阳江市" => "广东省",
"清远市" => "广东省",
"东莞市" => "广东省",
"中山市" => "广东省",
"潮州市" => "广东省",
"揭阳市" => "广东省",
"云浮市" => "广东省",
"南宁市" => "广西壮族自治区",
"柳州市" => "广西壮族自治区",
"桂林市" => "广西壮族自治区",
"梧州市" => "广西壮族自治区",
"北海市" => "广西壮族自治区",
"防城港市" => "广西壮族自治区",
"钦州市" => "广西壮族自治区",
"贵港市" => "广西壮族自治区",
"玉林市" => "广西壮族自治区",
"百色市" => "广西壮族自治区",
"贺州市" => "广西壮族自治区",
"河池市" => "广西壮族自治区",
"来宾市" => "广西壮族自治区",
"崇左市" => "广西壮族自治区",
"海口市" => "海南省",
"三亚市" => "海南省",
"三沙市" => "海南省",
"儋州市" => "海南省",
// 西南地区
"重庆市" => "重庆市",
"成都市" => "四川省",
"自贡市" => "四川省",
"攀枝花市" => "四川省",
"泸州市" => "四川省",
"德阳市" => "四川省",
"绵阳市" => "四川省",
"广元市" => "四川省",
"遂宁市" => "四川省",
"内江市" => "四川省",
"乐山市" => "四川省",
"南充市" => "四川省",
"眉山市" => "四川省",
"宜宾市" => "四川省",
"广安市" => "四川省",
"达州市" => "四川省",
"雅安市" => "四川省",
"巴中市" => "四川省",
"资阳市" => "四川省",
"阿坝藏族羌族自治州" => "四川省",
"甘孜藏族自治州" => "四川省",
"凉山彝族自治州" => "四川省",
"贵阳市" => "贵州省",
"六盘水市" => "贵州省",
"遵义市" => "贵州省",
"安顺市" => "贵州省",
"毕节市" => "贵州省",
"铜仁市" => "贵州省",
"黔西南布依族苗族自治州" => "贵州省",
"黔东南苗族侗族自治州" => "贵州省",
"黔南布依族苗族自治州" => "贵州省",
"昆明市" => "云南省",
"曲靖市" => "云南省",
"玉溪市" => "云南省",
"保山市" => "云南省",
"昭通市" => "云南省",
"丽江市" => "云南省",
"普洱市" => "云南省",
"临沧市" => "云南省",
"楚雄彝族自治州" => "云南省",
"红河哈尼族彝族自治州" => "云南省",
"文山壮族苗族自治州" => "云南省",
"西双版纳傣族自治州" => "云南省",
"大理白族自治州" => "云南省",
"德宏傣族景颇族自治州" => "云南省",
"怒江傈僳族自治州" => "云南省",
"迪庆藏族自治州" => "云南省",
"拉萨市" => "西藏自治区",
"日喀则市" => "西藏自治区",
"昌都市" => "西藏自治区",
"林芝市" => "西藏自治区",
"山南市" => "西藏自治区",
"那曲市" => "西藏自治区",
"阿里地区" => "西藏自治区",
// 西北地区
"西安市" => "陕西省",
"铜川市" => "陕西省",
"宝鸡市" => "陕西省",
"咸阳市" => "陕西省",
"渭南市" => "陕西省",
"延安市" => "陕西省",
"汉中市" => "陕西省",
"榆林市" => "陕西省",
"安康市" => "陕西省",
"商洛市" => "陕西省",
"兰州市" => "甘肃省",
"嘉峪关市" => "甘肃省",
"金昌市" => "甘肃省",
"白银市" => "甘肃省",
"天水市" => "甘肃省",
"武威市" => "甘肃省",
"张掖市" => "甘肃省",
"平凉市" => "甘肃省",
"酒泉市" => "甘肃省",
"庆阳市" => "甘肃省",
"定西市" => "甘肃省",
"陇南市" => "甘肃省",
"临夏回族自治州" => "甘肃省",
"甘南藏族自治州" => "甘肃省",
"西宁市" => "青海省",
"海东市" => "青海省",
"海北藏族自治州" => "青海省",
"黄南藏族自治州" => "青海省",
"海南藏族自治州" => "青海省",
"果洛藏族自治州" => "青海省",
"玉树藏族自治州" => "青海省",
"海西蒙古族藏族自治州" => "青海省",
"银川市" => "宁夏回族自治区",
"石嘴山市" => "宁夏回族自治区",
"吴忠市" => "宁夏回族自治区",
"固原市" => "宁夏回族自治区",
"中卫市" => "宁夏回族自治区",
"乌鲁木齐市" => "新疆维吾尔自治区",
"克拉玛依市" => "新疆维吾尔自治区",
"吐鲁番市" => "新疆维吾尔自治区",
"哈密市" => "新疆维吾尔自治区",
"昌吉回族自治州" => "新疆维吾尔自治区",
"博尔塔拉蒙古自治州" => "新疆维吾尔自治区",
"巴音郭楞蒙古自治州" => "新疆维吾尔自治区",
"阿克苏地区" => "新疆维吾尔自治区",
"克孜勒苏柯尔克孜自治州" => "新疆维吾尔自治区",
"喀什地区" => "新疆维吾尔自治区",
"和田地区" => "新疆维吾尔自治区",
"伊犁哈萨克自治州" => "新疆维吾尔自治区",
"塔城地区" => "新疆维吾尔自治区",
"阿勒泰地区" => "新疆维吾尔自治区",
// 港澳台地区
"台北市" => "台湾省",
"新北市" => "台湾省",
"桃园市" => "台湾省",
"台中市" => "台湾省",
"台南市" => "台湾省",
"高雄市" => "台湾省",
"基隆市" => "台湾省",
"新竹市" => "台湾省",
"嘉义市" => "台湾省"
];
// 检查市名是否存在于数组中
if (array_key_exists($city, $cityProvinceMap)) {
return $cityProvinceMap[$city];
} else {
return '';
}
}
function paresAddress($address){
preg_match('/(.*?(省|自治区|北京|天津|上海|重庆))/', $address, $matches);
if (count($matches) > 1) {
$province = $matches[count($matches) - 2];
$address = preg_replace('/(.*?(省|自治区|北京|天津|上海|重庆))/','', $address, 1);
}
preg_match('/(.*?(市|自治州|地区|区划|县))/', $address, $matches);
if (count($matches) > 1) {
$city = $matches[count($matches) - 2];
$address = str_replace($city, '', $address);
}
preg_match('/(.*?(区|县|镇|乡|街道))/', $address, $matches);
if (count($matches) > 1) {
$area = $matches[count($matches) - 2];
$address = str_replace($area, '', $address);
}
$city = isset($city) ? $city : '';
if (empty($province)){
$province = getprovince($city);
}
$area = isset($area) ? $area : '';
return [
'provice' => isset($province) ? $province : '',
'city' => $city,
"address" => $area.$address
];
}
/**
* 护照信息
* @param array $data
* @return string[]
*/
function parsePassport(array $data)
{
$res = [
'last_name' => '',
'first_name' => '',
'last_zh_name' => '',
'first_zh_name' => '',
'city_birth' => '',
'birth_date_day' => '',
'birth_date_month' => '',
'birth_date_year' => '',
'birth_date' => '',
'gender' => '',
'passport_number' => '',
'passport_expedition_date_day' => '',
'passport_expedition_date_month' => '',
'passport_expedition_date_year' => '',
'passport_expedition_date' => '',
'passport_expiration_date_day' => '',
'passport_expiration_date_month' => '',
'passport_expiration_date_year' => '',
'passport_expiration_date' => '',
'country_code'=>'',
'place_issue' => '',
'nationality' =>''
];
if (!is_array($data) || empty($data)) {
return $res;
}
if (!empty($data['words_result']['姓名拼音']['words'])) {
$names = explode(',', $data['words_result']['姓名拼音']['words']);
$res['last_name'] = $names[0] ?? '';
$res['first_name'] = $names[1] ?? '';
}
if (!empty($data['words_result']['姓名']['words'])) {
$zhnames = parseChineseName($data['words_result']['姓名']['words']);
$res['last_zh_name'] = $zhnames['surname'] ?? '';
$res['first_zh_name'] = $zhnames['givenName'] ?? '';
}
if (!empty($data['words_result']['国家码']['words'])) {
$res['country_code'] = $data['words_result']['国家码']['words'];
}
if (!empty($data['words_result']['护照签发地点']['words'])) {
$citys = explode('/', $data['words_result']['护照签发地点']['words']);
$res['place_issue'] = $citys[1] ?? '';
}
if (!empty($data['words_result']['国籍']['words'])) {
$citys = explode('/', $data['words_result']['国籍']['words']);
$res['nationality'] = $citys[1] ?? '';
}
if (!empty($data['words_result']['出生地点']['words'])) {
$citys = explode('/', $data['words_result']['出生地点']['words']);
$res['city_birth'] = $citys[1] ?? '';
}
if (!empty($data['words_result']['护照号码']['words'])) {
$res['passport_number'] = $data['words_result']['护照号码']['words'];
}
if (!empty($data['words_result']['签发日期']['words'])) {
$res['passport_expedition_date_year'] = substr($data['words_result']['签发日期']['words'], 0, 4);
$res['passport_expedition_date_month'] = intval(substr($data['words_result']['签发日期']['words'], 4, 2));
$res['passport_expedition_date_day'] = intval(substr($data['words_result']['签发日期']['words'], -2));
$res['passport_expedition_date'] = $res['passport_expedition_date_year'] . '-' . $res['passport_expedition_date_month'] . '-' . $res['passport_expedition_date_day'];
}
if (!empty($data['words_result']['有效期至']['words'])) {
$res['passport_expiration_date_year'] = substr($data['words_result']['有效期至']['words'], 0, 4);
$res['passport_expiration_date_month'] = intval(substr($data['words_result']['有效期至']['words'], 4, 2));
$res['passport_expiration_date_day'] = intval(substr($data['words_result']['有效期至']['words'], -2));
$res['passport_expiration_date'] = $res['passport_expiration_date_year'] . '-' . $res['passport_expiration_date_month'] . '-' . $res['passport_expiration_date_day'];
}
if (!empty($data['words_result']['生日']['words'])) {
$res['birth_date_year'] = substr($data['words_result']['生日']['words'], 0, 4);
$res['birth_date_month'] = intval(substr($data['words_result']['生日']['words'], 4, 2));
$res['birth_date_day'] = intval(substr($data['words_result']['生日']['words'], -2));
$res['birth_date'] = $res['birth_date_year'] . '-' . $res['birth_date_month'] . '-' . $res['birth_date_day'];
}
if (!empty($data['words_result']['性别']['words'])) {
$names = explode('/', $data['words_result']['性别']['words']);
$res['gender'] = ($names[0] == '男') ? 'male' : 'female';
}
return $res;
}
/**
* 解析中文姓名为姓和名
*
* @param string $name 中文姓名
* @return array 包含姓和名的数组 ['surname' => '姓', 'givenName' => '名']
*/
function parseChineseName($name) {
// 移除姓名前后的空格
$name = trim($name);
// 如果姓名长度小于2无法合理分割返回原姓名作为姓氏
if (mb_strlen($name, 'UTF-8') < 2) {
return ['surname' => $name, 'givenName' => ''];
}
// 常见复姓列表(可根据需要扩展)
$compoundSurnames = [
'欧阳', '太史', '端木', '上官', '司马', '东方', '独孤', '南宫', '万俟',
'闻人', '夏侯', '诸葛', '尉迟', '公羊', '赫连', '澹台', '皇甫', '宗政',
'濮阳', '公冶', '太叔', '申屠', '公孙', '慕容', '仲孙', '钟离', '长孙',
'宇文', '司徒', '鲜于', '司空', '闾丘', '子车', '亓官', '司寇', '巫马',
'公西', '颛孙', '壤驷', '公良', '漆雕', '乐正', '宰父', '谷梁', '拓跋',
'夹谷', '轩辕', '令狐', '段干', '百里', '呼延', '东郭', '南门', '羊舌',
'微生', '公户', '公玉', '公仪', '梁丘', '公仲', '公上', '公门', '公山',
'公坚', '左丘', '公伯', '西门', '公祖', '第五', '公乘', '贯丘', '公皙',
'南荣', '东里', '东宫', '仲长', '子书', '子桑', '即墨', '达奚', '褚师'
];
// 检查是否是复姓
$firstTwoChars = mb_substr($name, 0, 2, 'UTF-8');
if (in_array($firstTwoChars, $compoundSurnames)) {
return [
'surname' => $firstTwoChars,
'givenName' => mb_substr($name, 2, null, 'UTF-8')
];
}
// 默认情况:第一个字符为姓,其余为名
return [
'surname' => mb_substr($name, 0, 1, 'UTF-8'),
'givenName' => mb_substr($name, 1, null, 'UTF-8')
];
}
/**
* 签证信息
* @param array $data
* @return string[]
*/
function parseVisa(array $data)
{
$res = [
'visa_foil_number' => '',
'passport_number' => '',
];
if (!is_array($data) || empty($data)) {
return $res;
}
foreach ($data['words_result'] as $arr) {
$str = preg_replace('/\s/i', '', $arr['words']);
if (preg_match('/^[G|E|P]([a-zA-Z0-9]{8})$/i', $str)) {
$res['passport_number'] = $str;
} elseif (preg_match('/^(P|H|J|K|L|M|N)\d{7}$/i', $str)) {
$res['visa_foil_number'] = $str;
}
}
return $res;
}
// 这是系统自动生成的公共文件

View File

@@ -0,0 +1,163 @@
<?php
declare (strict_types=1);
namespace app\home\controller;
use api\Httpcurl;
use app\BaseController;
use think\facade\Config;
use think\facade\Cookie;
use think\facade\Lang;
use think\facade\Log;
use think\facade\Request;
use think\facade\Session;
use think\facade\View;
/**
* 控制器基础类
*/
class Base extends BaseController
{
protected $_userId;
/**
* 构造方法
*/
public function initialize()
{
parent::initialize();
$source = Request::instance()->param('source', '');
if (!empty($source)) {
Session::set('source', $source);
} else {
$source = Session::get('source');
if (empty($source)) {
Session::set('source', $_SERVER['HTTP_HOST']);
} else {
Session::set('source', $source);
}
}
// 获取 当前用户ID
$this->_userId = cmf_get_current_user_id();
// 网站配置信息获取
if (!file_exists(app()->getRootPath().'data/config.json')) {
$domain = $_SERVER['HTTP_HOST'];
$web_config = Httpcurl::request(GET_WEB_CONFIG_URL . MODEL . '/domain/' . $domain, 'get');
if ($web_config[3]) {
abort(500, '网站配置信息获取失败');
}
$config_result = json_decode($web_config[0], true);
if (empty($config_result)) {
abort(500, '网站配置信息获取失败');
}
if ($config_result['code'] != 1) {
abort(500, '网站配置信息获取失败');
}
file_put_contents(app()->getRootPath().'data/config.json', json_encode($config_result));
} else {
$config_result = json_decode(file_get_contents(app()->getRootPath().'data/config.json'), true);
}
Lang::setLangSet(Cookie::get('think_var', 'zh-cn'));
View::assign('think_lang', Cookie::get('think_var'));
View::assign('menu_url', strtolower(Request::controller() . '/' . Request::action()));
/* URL 配置*/
Config::set(['GET_ORDER_DETAIL_URL' => $config_result['data']['get_order_detail_url']], 'app');
Config::set(['LOOKUP_URL' => $config_result['data']['lookup_url']], 'app');
Config::set(['CREATE_ORDER_URL' => $config_result['data']['create_order_url']], 'app');
Config::set(['NEWS_LIST' => $config_result['data']['news_list']], 'app');
Config::set(['NEWS_DETAIL' => $config_result['data']['news_detail']], 'app');
Config::set(['CREATE_CONTACT_URL' => $config_result['data']['create_contact_url']], 'app');
Config::set(['GET_ORDER_URL' => $config_result['data']['get_order_url']], 'app');
Config::set(['YZM_URL' => $config_result['data']['yzm_url']], 'app');
/*ocr 识别 配置参数 */
Config::set(['OCR_BASE_URL' => $config_result['data']['ocr_base_url']], 'app');
Config::set(['OCR_APP_ID' => $config_result['data']['ocr_app_id']], 'app');
Config::set(['OCR_SECRET' => $config_result['data']['ocr_secret']], 'app');
$ocr_config = build_ocr_signature($config_result['data']['ocr_app_id'], $config_result['data']['ocr_secret']);
View::assign('ocr_config', json_encode($ocr_config));
/* 国内支付配置*/
Config::set(['wechatpay' => $config_result['data']['pay']['wechatpay']], 'app');
Config::set(['wechath5pay' => $config_result['data']['pay']['wechath5pay']], 'app');
Config::set(['wechatpcpay' => $config_result['data']['pay']['wechatpcpay']], 'app');
Config::set(['alipcpay' => $config_result['data']['pay']['alipcpay']], 'app');
Config::set(['user_url' => $config_result['data']['user_url']], 'app');
Config::set(['order_url' => $config_result['data']['order_url']], 'app');
View::assign('company_address', $config_result['data']['company_address']);
View::assign('company_phone', $config_result['data']['phone'] ?? '');
View::assign('base_path', BASE_PATH);
View::assign('fapiao_kefu', $config_result['data']['fapiao_png']);
if (Cookie::get('think_var') == 'en-us') {
View::assign('footer', $config_result['data']['footer_en']);
View::assign('goods', $config_result['data']['MALA_GOODS_EN']);
View::assign('country', COUNTRY_EN);
View::assign('phone_prefix', json_decode(file_get_contents(CONFIG_JSON_PHONE_PREFIX_EN), true));
View::assign('travel_country', json_decode(file_get_contents(CONFIG_JSON_TRAVEL_COUNTRY_EN), true));
} else {
View::assign('phone_prefix', json_decode(file_get_contents(CONFIG_JSON_PHONE_PREFIX), true));
View::assign('travel_country', json_decode(file_get_contents(CONFIG_JSON_TRAVEL_COUNTRY), true));
View::assign('country', COUNTRY);
View::assign('footer', $config_result['data']['footer']);
View::assign('goods', $config_result['data']['MALA_GOODS']);
}
}
public function langs($lang='zh-cn')
{
Cookie::set('think_var', $lang);
return true;
}
public function success($msg = '操作成功', $url = '', $data = [], $wait = 3)
{
if (is_null($url) && isset($_SERVER["HTTP_REFERER"])) {
$url = $_SERVER["HTTP_REFERER"];
} elseif ('' !== $url && is_object($url)) {
$url = $url->build();
}
if (request()->isAjax()) {
return json([
'code' => 1,
'msg' => $msg,
'data' => $data,
'url' => $url
]);
} else {
return View::fetch('public/jump', [
'msg' => $msg,
'url' => $url,
'wait' => $wait,
'type' => 'success',
]);
}
}
public function error($msg = '操作失败', $url = '', $wait = 3, $data = [])
{
if (is_null($url) && isset($_SERVER["HTTP_REFERER"])) {
$url = $_SERVER["HTTP_REFERER"];
} elseif ('' !== $url && is_object($url)) {
$url = $url->build();
}
if (request()->isAjax()) {
return json([
'code' => 0,
'msg' => $msg,
'data' => $data,
'url' => $url
]);
} else {
return View::fetch('public/jump', [
'msg' => $msg,
'url' => $url,
'wait' => $wait,
'type' => 'error',
]);
}
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace app\home\controller;
use api\Httpcurl;
use think\facade\Cookie;
use think\facade\Db;
use think\facade\View;
class Contact extends Base
{
public function index()
{
if (Cookie::get('think_var') == 'en-us') {
View::assign('reason', CONTACT_REASON_EN);
} else {
View::assign('reason', CONTACT_REASON);
}
return View::fetch();
}
public function do_apply()
{
if(!captcha_check($_REQUEST['yzm'])){
return $this->error(lang('contact.yzm_error'));
}
if ($_REQUEST['email'] !== $_REQUEST['re_email']) {
return $this->error(lang('contact.email_error'));
}
unset($_REQUEST['re_email']);
unset($_REQUEST['s']);
unset($_REQUEST['yzm']);
$_REQUEST['reg_time'] = date('Y-m-d H:i:s');
$_REQUEST['source'] = MODEL;
$_REQUEST['model'] = CONTACT_MODEL;
$_REQUEST['lang'] = Cookie::get('think_var');
$_REQUEST['token'] = md5(CONTACT_MODEL);
try {
$order_res = Httpcurl::request(config('app.CREATE_CONTACT_URL'), 'post', $_REQUEST);
$order = json_decode($order_res[0], true);
if (!$order['code']) {
return $this->error($order['msg']);
}
return $this->success(lang('controller.success'));
} catch (\Exception $e) {
return $this->error(lang('controller.fail'));
}
}
}

View File

@@ -0,0 +1,24 @@
<?php
declare (strict_types = 1);
namespace app\home\controller;
use think\facade\View;
class Index extends Base
{
public function index()
{
return View::fetch();
}
public function langset()
{
$lang = input('lang') ? input('lang') : 'zh-cn';
parent::langs($lang);
return json([
'code' => 1,
'msg' => lang('controller.success'),
]);
}
}

View File

@@ -0,0 +1,92 @@
<?php
namespace app\home\controller;
use api\Httpcurl;
use think\exception\HttpResponseException;
use think\facade\Config;
use think\facade\Log;
use think\facade\Request;
use think\Response;
class Invoice extends Base
{
public function initialize()
{
parent::initialize();
if (!$this->_userId) {
throw new HttpResponseException(Response::create($this->error('请先登录', url('user/index'))));
}
}
/**个人中心
*/
public function index()
{
//获取用户的登记信息
$order_sn = Request::instance()->param('order_sn');
if (empty($order_sn)) {
return $this->error('参数错误');
}
$base_url = config('app.order_url');
$order_url = $base_url . 'getOrder';
$response = Httpcurl::request($order_url, 'post', ['order_sn' => $order_sn, 'model' => MODEL]);
if ($response[3]) {
return $this->error('异常错误,请重新提交');
}
$pay_result = json_decode($response[0], true);
if (empty($pay_result)) {
return $this->error('异常错误,请重新提交');
}
Log::info(json_encode($pay_result));
if (!$pay_result['code']) {
return $this->error($pay_result['msg']);
}
$data = $pay_result['data'];
if (empty($data)) {
return $this->error('无此数据');
}
$total_price = $data['total_price'] ? $data['total_price'] : $data['origin_price'];
$data['total_price'] = ($total_price / 100) . "";
return view('', [
'data' => $data,
]);
}
public function add_invoice()
{
$res = [
'status' => 0,
'msg' => '操作失败,请重新填写'
];
unset($_REQUEST['s']);
$_REQUEST['invoiceType'] = INCOICETYPE;
$_REQUEST['model'] = MODEL;
$_REQUEST['principals'] = config('app.principals');
$_REQUEST['uid'] = $this->_userId;
$base_url = config('app.order_url');
$order_url = $base_url . 'add_invoice';
$response = Httpcurl::request($order_url, 'post', $_REQUEST);
if ($response[3]) {
return json($res);
}
$pay_result = json_decode($response[0], true);
Log::info(json_encode($pay_result));
if (empty($pay_result)) {
return json($res);
}
if (!$pay_result['code']) {
$res['msg'] = $pay_result['msg'];
return json($res);
}
$res['status'] = 1;
$res['msg'] = $pay_result['msg'];
$res['url'] = url('user/index')->build();
return json($res);
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace app\home\controller;
use api\Httpcurl;
use think\facade\Log;
use think\facade\View;
class Lookup extends Base
{
public function index()
{
return View::fetch();
}
public function enrollment()
{
$data = request()->post();
$map = [
'passport_number' => $data['passport_number'],
'birth_date' => $data['birth_date'],
'last_name' => $data['last_name'],
'first_name' => $data['first_name'],
'model' => MODEL,
];
$res = Httpcurl::request(config('app.LOOKUP_URL'), 'post', $map);
$res = json_decode($res[0], true);
Log::info(json_encode($res));
if (empty($res)) {
return json(['code' => 0, 'msg' => lang("controller.no_records_found")]);
}
if ($res['code'] == 0) {
return json(['code' => 0, 'msg' => $res['msg']]);
}
return json(['code' => 1, 'msg' => $res['msg'], 'data' => $res['data']]);
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace app\home\controller;
use api\Httpcurl;
use think\facade\Log;
use think\facade\View;
use think\Request;
class News extends Base
{
public function index()
{
$page = input('page') ? input('page') : 1;
$size = input('size') ? input('size') : 10;
$news_list = Httpcurl::request(config('app.NEWS_LIST'), 'post', [
'type' => MODEL,
'page' => $page,
'size' => $size,
]);
$news_list = json_decode($news_list[0], true);
Log::info(json_encode($news_list));
if ($news_list['code'] == 1) {
View::assign('news_list', $news_list['data']);
View::assign('count', $news_list['count']);
View::assign('page', $page);
View::assign('size', $size);
} else {
View::assign('news_list', []);
}
return View::fetch('news');
}
public function detail(Request $request)
{
$data = $request->param();
if (empty($data['id'])) {
return $this->error('参数错误');
}
$info = Httpcurl::request(config('app.NEWS_DETAIL'), 'post', [
'type' => MODEL,
'id' => $data['id'],
]);
$info = json_decode($info[0], true);
Log::info(json_encode($info));
$info_detail = [];
if ($info['code'] == 1) {
$info_detail = $info['data'];
}
View::assign('info', $info_detail);
return View::fetch('news_detail');
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace app\home\controller;
use think\facade\View;
class Policy extends Base
{
public function cookie()
{
return View::fetch();
}
public function disclaimer()
{
return View::fetch();
}
public function privacy()
{
return View::fetch();
}
public function pay()
{
return View::fetch();
}
public function refund()
{
return View::fetch();
}
public function user()
{
return View::fetch();
}
}

View File

@@ -0,0 +1,199 @@
<?php
declare (strict_types=1);
namespace app\home\controller;
use app\service\AwsUploadService;
use app\service\BaiduOcrService;
use app\service\CompressImgService;
use app\service\SwooleService;
use think\facade\Log;
use think\file\UploadedFile;
class Upload extends Base
{
public function upload_passport()
{
// 获取上传的文件
$file = request()->file('file');
$path = request()->param('path', '');
if (!$file) {
return json([
'code' => 0,
'msg' => lang('controller.upload_missing'),
]);
}
$imgdata = file_get_contents($file->getPathname());
/*同时上传护照文件*/
$fileName = $path . '/' . date('YmdHis') . uniqid() . '.' . pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
try {
$client = SwooleService::getInstance();
$client->sendTask([
'type' => 'upload_ali',
'data' => [
'Bucket' => BUCKET,
'Key' => $fileName, //重命名的文件名
'file_content' => base64_encode($imgdata),
]
]);
$client->close();
return json([
'code' => 1,
'msg' => lang('controller.upload_success'),
'path' => OSS_URL . $fileName,
]);
} catch (\Exception $e) {
return json([
'code' => 0,
'msg' => lang('controller.upload_failed'),
'path' => ''
]);
}
}
public function visa()
{
// 获取上传的文件
$file = request()->file('file');
$path = request()->param('path', '');
if (!$file) {
return json([
'code' => 0,
'msg' => lang('controller.upload_missing'),
]);
}
$imgdata = file_get_contents($file->getPathname());
/*同时上传签证文件*/
$fileName = $path . '/' . date('YmdHis') . uniqid() . '.' . pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
try {
$client = SwooleService::getInstance();
$client->sendTask([
'type' => 'upload_ali',
'data' => [
'Bucket' => BUCKET,
'Key' => $fileName, //重命名的文件名
'file_content' => base64_encode($imgdata),
]
]);
$client->close();
return json([
'code' => 1,
'msg' => lang('controller.upload_success'),
'path' => OSS_URL . $fileName,
]);
} catch (\Exception $e) {
return json([
'code' => 0,
'msg' => lang('controller.upload_failed'),
'path' => ''
]);
}
}
public function identification()
{
// 获取上传的文件
$file = request()->file('file');
$path = request()->param('path', '');
if (!$file) {
return json([
'code' => 0,
'msg' => lang('controller.upload_missing'),
]);
}
$imgdata = file_get_contents($file->getPathname());
/*同时上传身份证文件*/
$fileName = $path . '/' . date('YmdHis') . uniqid() . '.' . pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
try {
$client = SwooleService::getInstance();
$client->sendTask([
'type' => 'upload_ali',
'data' => [
'Bucket' => BUCKET,
'Key' => $fileName, //重命名的文件名
'file_content' => base64_encode($imgdata),
]
]);
$client->close();
return json([
'code' => 1,
'msg' => lang('controller.upload_success'),
'path' => OSS_URL . $fileName,
]);
} catch (\Exception $e) {
return json([
'code' => 0,
'msg' => lang('controller.upload_failed'),
'path' => ''
]);
}
}
public function awsUpload($file, $fileName)
{
$fileSize = $file->getSize();
$originalName = $file->getOriginalName();
$extension = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
$sizeLimit = 3 * 1024 * 1024;
if ($fileSize > $sizeLimit && in_array($extension, ['jpg', 'jpeg', 'png', 'gif'])) {
$compressedFilePath = (new CompressImgService())->compressImage($file->getRealPath(), $fileSize);
$file = new UploadedFile(
$compressedFilePath,
pathinfo($originalName, PATHINFO_FILENAME) . '_compressed.' . $extension,
mime_content_type($compressedFilePath) ?: $file->getMime(),
0,
true
);
}
$service = new AwsUploadService();
// 额外传递的字段
$extraFields = [
'Bucket' => BUCKET,
'Key' => $fileName //重命名的文件名
];
$result = $service->uploadAndForward($file, 'file', $extraFields);
Log::info(json_encode($result));
return $result;
}
public function file_upload()
{
// 获取上传的文件
$file = request()->file('file');
$path = request()->param('path', '');
if (!$file) {
return json([
'code' => 0,
'msg' => lang('controller.upload_missing'),
]);
}
$imgdata = file_get_contents($file->getPathname());
$fileName = $path . '/' . date('YmdHis') . uniqid() . '.' . pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
try {
$client = SwooleService::getInstance();
$client->sendTask([
'type' => 'upload_ali',
'data' => [
'Bucket' => BUCKET,
'Key' => $fileName, //重命名的文件名
'file_content' => base64_encode($imgdata),
]
]);
$client->close();
return json([
'code' => 1,
'msg' => lang('controller.upload_success'),
'path' => OSS_URL . $fileName,
]);
} catch (\Exception $e) {
return json([
'code' => 0,
'msg' => lang('controller.upload_failed'),
'path' => '',
]);
}
}
}

View File

@@ -0,0 +1,200 @@
<?php
namespace app\home\controller;
use api\Httpcurl;
use think\facade\Log;
use think\facade\Request;
class User extends Base
{
public function index()
{
if (!empty($this->_userId)) {
$paystatus = Request::instance()->param('paystatus', '');
$paystatus = $paystatus ? $paystatus : 'pay';
if (!in_array($paystatus, ['pay', 'nopay'])) {
return $this->error('参数错误');
}
$list = Httpcurl::request(config('app.GET_ORDER_URL'), 'post', [
'model' => MODEL,
'user_id' => $this->_userId,
'invoiceType' => INCOICETYPE,
'pay_status' => '',
]);
$result = json_decode($list[0], true);
Log::info(json_encode($result));
if (!$result['code']) {
return $this->error('获取订单失败');
}
return view('', [
'paystauts' => $paystatus,
'order_list' => $result['data'],
]);
} else {
return view('login');
}
}
public function invoice()
{
return view('invoice');
}
public function multiple()
{
return view('multiple');
}
public function checkInvoice()
{
$res = [
'status' => 0,
'msg' => '操作失败'
];
$passport_number = Request::instance()->param('passport_number');
$order_sn = Request::instance()->param('order_sn');
$last_name = Request::instance()->param('last_name');
$first_name = Request::instance()->param('first_name');
if (empty($passport_number) || empty($order_sn) || empty($last_name) || empty($first_name)) {
$res['msg'] = '参数错误';
return json($res);
}
$base_url = config('app.order_url');
$order_url = $base_url . 'checkInvoice';
$response = Httpcurl::request($order_url, 'post', [
'passport_number' => trim($passport_number),
'order_sn' => trim($order_sn),
'last_name' => trim($last_name),
'first_name' => trim($first_name),
'model' => MODEL
]);
if ($response[3]) {
$res['msg'] = '参数错误';
return json($res);
}
$data = json_decode($response[0], true);
Log::info(json_encode($data));
if (empty($data)) {
$res['msg'] = '系统错误,请联系管理员!';
return json($res);
}
if (!$data['code']) {
$res['msg'] = "暂无订单信息";
return json($res);
}
$invoice_url = $base_url . 'getInvoice';
//判断是否已经开票了
$response = Httpcurl::request($invoice_url, 'post', [
'order_sn' => $order_sn,
'invoiceType' => INCOICETYPE,
'model' => MODEL
]);
if ($response[3]) {
$res['msg'] = '参数错误';
return json($res);
}
$order = json_decode($response[0], true);
Log::info(json_encode($order));
if (empty($order)) {
$res['msg'] = '系统错误,请联系管理员!';
return json($res);
}
if (!$order['code']) {
return json($res);
}
if (!empty($order['data'])) {
$res['msg'] = '已经开票';
return json($res);
}
$res = [
'status' => 1,
'msg' => '操作成功',
'url' => url('home/invoice/index', ['order_sn' => $order_sn])->build()
];
return json($res);
}
public function checkMultiple()
{
$res = [
'status' => 0,
'msg' => '操作失败'
];
$datas = Request::instance()->param('datas');
if (empty($datas)) {
$res['msg'] = '参数错误';
return json($res);
}
$datas = json_decode(html_entity_decode(stripslashes($datas)), true);
if (empty($datas)) {
$res['msg'] = '数据错误';
return json($res);
}
$order_sns = array_column($datas, 'order_sn');
//判断是否有重复订单
if (count($order_sns) != count(array_unique($order_sns))) {
$res['msg'] = '有重复订单,请删除后重新提交';
return json($res);
}
$base_url = config('app.order_url');
$order_url = $base_url . 'checkInvoice';
$invoice_url = $base_url . 'getInvoice';
foreach ($datas as $da) {
//获取信息
$response = Httpcurl::request($order_url, 'post', [
'passport_number' => trim($da['passport_number']),
'order_sn' => trim($da['order_sn']),
'last_name' => trim($da['last_name']),
'first_name' => trim($da['first_name']),
'model' => MODEL
]);
if ($response[3]) {
$res['msg'] = '参数错误';
return json($res);
}
$data = json_decode($response[0], true);
Log::info(json_encode($data));
if (empty($data)) {
$res['msg'] = '未查到订单信息';
return json($res);
}
//查询是否已经开票了
$response = Httpcurl::request($invoice_url, 'post', [
'order_sn' => trim($da['order_sn']),
'invoiceType' => INCOICETYPE,
'model' => MODEL
]);
if ($response[3]) {
$res['msg'] = '参数错误';
return json($res);
}
$order = json_decode($response[0], true);
Log::info(json_encode($order));
if (empty($order)) {
$res['msg'] = '系统错误,请联系管理员!';
return json($res);
}
if (!$order['code']) {
$res['msg'] = $order['msg'];
return json($res);
}
if (!empty($order['data'])) {
$res['msg'] = '订单号:' . $da['order_sn'] . '已经开票';
return json($res);
}
}
$order_sns = implode('-', $order_sns);
$res = [
'status' => 1,
'msg' => '操作成功',
'url' => url('home/multiple/index', ['id' => base64_encode($order_sns)])->build()
];
return json($res);
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace app\home\controller;
use api\Httpcurl;
use think\facade\Cache;
use think\facade\Log;
use think\facade\Session;
use think\facade\View;
use think\Request;
class Visa extends Base
{
public function index()
{
View::assign('nation', json_decode(file_get_contents(CONFIG_JSON_VISA_NATIONALITY), true));
View::assign('ismobile', isMobile() ? 1 : 0);
return View::fetch();
}
public function get_sub_city(Request $request)
{
$id = $request->param('id');
$city = find_json(CONFIG_JSON_STATE, 'id', $id);
if ($city) {
return $this->success('成功', '', $city[0]);
} else {
return $this->success('成功', '', []);
}
}
public function do_apply()
{
$request = $this->request;
$data = $request->post();
$order_sn = getOrderNumber();
unset($data['check1']);
unset($data['check2']);
$data['model'] = MODEL;
$data['token'] = md5(MODEL);
$data['order_sn'] = $order_sn;
$data['source'] = Session::get('source');
$data['is_mobile'] = isMobile();
/*处理 map */
$nationality_map = json_decode(file_get_contents(CONFIG_JSON_NATIONALITY_MAP), true) ?? [];
$data['nation'] = $nationality_map[$data['nation']] ?? $data['nation'];
$order_res = Httpcurl::request(config('app.CREATE_ORDER_URL'), 'post', $data);
$order = json_decode($order_res[0], true);
Log::info(json_encode($order));
if (!$order['code']) {
return $this->error(lang('controller.submission_failed'));
}
Session::set('order_sn', $order_sn);
Session::set('total_price', $data['total_price']);
$url = url('wepay/pcpay', ['order_sn' => $order_sn]);
if (isMobile()) {
if (strpos($_SERVER['HTTP_USER_AGENT'], 'MicroMessenger') !== false) {
$url = url('wepay/wxpay', ['order_sn' => $order_sn]);
}else{
$url = url('wepay/h5pay', ['order_sn' => $order_sn]);
}
}
return $this->success(lang('controller.success'), $url);
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace app\home\controller;
use api\Httpcurl;
use think\facade\Request;
class Wechatpay extends Base
{
public function index()
{
$order_sn = Request::instance()->param('order_sn');
if (!$order_sn) {
return $this->error(lang('wepay.missing_order_sn'), url('visa/index'));
}
$response = Httpcurl::request(config('app.GET_ORDER_DETAIL_URL'), 'post', ['order_sn' => $order_sn, 'model' => MODEL]);
if ($response[3]) {
return $this->error(lang('wepay.payment_failed_retry'), url('visa/index'));
}
$pay_result = json_decode($response[0], true);
if (empty($pay_result)) {
return $this->error(lang('wepay.payment_failed_retry'), url('visa/index'));
}
if (!$pay_result['code']) {
return $this->error(lang('wepay.payment_failed_retry'), url('visa/index'));
}
$pay_result = $pay_result['data'];
if ($pay_result['pay_status']) {
return $this->success(lang('wepay.payment_success'), url('wepay/succ'), ['order_sn' => $order_sn]);
}
return $this->error(lang('wepay.payment_failed_retry'), url('visa/index'));
}
}

View File

@@ -0,0 +1,185 @@
<?php
namespace app\home\controller;
use api\Httpcurl;
use think\exception\HttpResponseException;
use think\facade\Log;
use think\facade\Request;
use think\facade\View;
use think\Response;
class Wepay extends Base
{
private $order_sn;
public function initialize()
{
parent::initialize();
$order_sn = input('order_sn');
if (empty($order_sn)) {
$order_sn = input('reference');
}
if (!$order_sn) {
throw new HttpResponseException(Response::create($this->error(lang('wepay.missing_order_sn'), url('visa/index'))));
}
$this->order_sn = $order_sn;
}
public function wxpay()
{
$wechaturl = config('app.wechatpay');
$wechaturl .= '?order_sn=' . $this->order_sn . '&mark=' . PAY_MARK . '&body=' . urlencode(COUNTRY . lang('wepay.pay_body'));
header('location:' . $wechaturl);
exit;
}
public function pcpay()
{
$order_sn = $this->order_sn;
$response = Httpcurl::request(config('app.GET_ORDER_DETAIL_URL'), 'post', ['order_sn' => $order_sn, 'model' => MODEL]);
if ($response[3]) {
return $this->error(lang('wepay.system_error'), url('visa/index'));
}
$pay_result = json_decode($response[0], true);
Log::info(json_encode($pay_result));
if (empty($pay_result)) {
return $this->error(lang('wepay.system_error'), url('visa/index'));
}
if (!$pay_result['code']) {
return $this->error($pay_result['msg'], url('visa/index'));
}
$pay_result = $pay_result['data'];
if ($pay_result['pay_status']) {
return $this->success(lang('wepay.already_paid'), url('visa/index'));
}
$body = COUNTRY . lang('wepay.pay_body');
$wxurl = '';
$res = Httpcurl::request(config('app.wechatpcpay'), 'get', ['order_sn' => $order_sn, 'mark' => PAY_MARK, 'body' => $body]);
if (!$res[3]) {
$result = json_decode($res[0], true);
$wxurl = $result['url'] ?? '';
}
Log::info(json_encode($res));
$aliurl = '';
$res = Httpcurl::request(config('app.alipcpay'), 'get', ['order_sn' => $order_sn, 'mark' => PAY_MARK, 'body' => urlencode($body)]);
if (!$res[3]) {
$result = json_decode($res[0], true);
$aliurl = $result['url'] ?? '';
}
Log::info(json_encode($res));
View::assign('url', $wxurl);
View::assign('aliurl', $aliurl);
View::assign('order_sn', $order_sn);
View::assign('total_fee', number_format(($pay_result['total_price'] ?? 0) / 100, 2));
return View::fetch();
}
public function h5pay()
{
$wechaturl = config('app.wechath5pay');
$wechaturl .= '?order_sn=' . $this->order_sn . '&mark=' . PAY_MARK . '&body=' . urlencode(COUNTRY . lang('wepay.pay_body'));
header('location:' . $wechaturl);
exit;
}
public function succ()
{
/*判断订单状态*/
$order_info = Httpcurl::request(config('app.GET_ORDER_DETAIL_URL'), 'post', ['order_sn' => $this->order_sn, 'model' => MODEL]);
$order_info = json_decode($order_info[0], true);
if ($order_info['code'] == '0') {
return $this->error(lang("controller.order_not_exist"));
}
$order_info = $order_info['data'];
if ($order_info) {
if ($order_info['pay_status'] == 0 ) {
return $this->success(lang("controller.payment_not_completed"), url('visa/index'));
}
}
/*判断订单状态*/
$arrive_date = $order_info['arrive_date'] ?? '2026-05-30';
$arrive_timestamp = $arrive_date ? strtotime($arrive_date) : false;
$latest_submit_timestamp = strtotime(date('Y-m-d') . ' +' . ARRIVE_DATE_COUNT . ' days 23:59:59');
if ($arrive_timestamp === false || $arrive_timestamp < $latest_submit_timestamp) {
View::assign('arrive_date_info', '');
} else {
$arrive_date_info = sprintf(lang("pay.arrive_date_info"), $arrive_date, date('Y-m-d', $arrive_timestamp - ARRIVE_DATE_COUNT * 86400));
View::assign('arrive_date_info', $arrive_date_info);
}
View::assign('payment_confirmed', true);
View::assign('poll_status_url', '');
View::assign('repay_url', '');
View::assign('order_sn', $this->order_sn);
return View::fetch();
}
public function h5result()
{
if (!Request::instance()->isAjax()) {
return View::fetch();
}
return $this->checkPayStatus('h5pay');
}
public function pcresult()
{
if (!Request::instance()->isAjax()) {
return null;
}
return $this->checkPayStatus('pcpay');
}
private function checkPayStatus(string $retryAction)
{
$order_sn = $this->order_sn;
$response = Httpcurl::request(config('app.GET_ORDER_DETAIL_URL'), 'post', ['order_sn' => $order_sn, 'model' => MODEL]);
if ($response[3]) {
return json([
'status' => 0,
'msg' => lang('wepay.system_error'),
'url' => url('wepay/index', ['order_sn' => $order_sn])->build(),
]);
}
$pay_result = json_decode($response[0], true);
Log::info(json_encode($pay_result));
if (empty($pay_result)) {
return json([
'status' => 0,
'msg' => lang('wepay.system_error'),
'url' => url('wepay/index', ['order_sn' => $order_sn])->build(),
]);
}
if (!$pay_result['code']) {
return json([
'status' => 0,
'msg' => $pay_result['msg'],
'url' => url('wepay/index', ['order_sn' => $order_sn])->build(),
]);
}
$pay_result = $pay_result['data'];
if ($pay_result['pay_status']) {
return json([
'status' => 1,
'msg' => lang('wepay.payment_success'),
'url' => url('wepay/succ', ['order_sn' => $order_sn])->build(),
]);
}
return json([
'status' => 0,
'msg' => lang('wepay.payment_not_completed'),
'url' => url('wepay/' . $retryAction, ['order_sn' => $order_sn])->build(),
]);
}
}

5
app/home/event.php Normal file
View File

@@ -0,0 +1,5 @@
<?php
// 这是系统自动生成的event定义文件
return [
];

5
app/home/middleware.php Normal file
View File

@@ -0,0 +1,5 @@
<?php
// 这是系统自动生成的middleware定义文件
return [
];

View File

@@ -0,0 +1,441 @@
{include file="public/head" /}
<link rel="stylesheet" href="/static/css/visa.css?v={:time()}">
<style>
body { background-color: #f5f5f5; }
/* ===== 页面容器:居中卡片风格 ===== */
.contact-card{
background: #fff;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,.06);
padding: 28px 30px 22px;
border: 1px solid rgba(0,0,0,.06);
}
.contact-title h2{
font-size: 34px;
font-weight: 800;
color: #111;
margin: 0 0 10px;
}
.contact-desc{
color: #666;
font-size: 14px;
margin: 0 0 18px;
}
.contact-hr{
height: 1px;
background: #eee;
margin: 16px 0 22px;
}
/* ===== 表单:标签在上(截图同款) ===== */
.layui-form-item{ margin-bottom: 18px; }
.layui-form-label{
float: none;
width: 100%;
text-align: left;
padding: 0 0 8px 0;
font-weight: 700;
color: #222;
}
.layui-input-block{
margin-left: 0;
min-height: auto;
}
.layui-form-label span{
color: #e53935;
margin-left: 4px;
font-weight: 800;
}
/* 输入框更接近截图(高度、边框、圆角) */
.layui-input, .layui-textarea, .layui-select{
border-radius: 4px;
border-color: #e6e6e6;
}
.layui-input{ height: 44px; line-height: 44px; }
.layui-textarea{ min-height: 150px; padding: 10px 12px; }
/* ===== 输入组:做成 flex验证码在右侧不挤压 ===== */
.layui-input-group{
display: flex;
align-items: center;
gap: 12px;
width: 100%;
}
.layui-input-group > .layui-input,
.layui-input-group > input.layui-input{
flex: 1 1 auto;
min-width: 0;
}
.layui-input-group .layui-input-group-prepend{
flex: 0 0 auto;
width: auto;
}
.captcha-img-container{
display: flex;
align-items: center;
gap: 10px;
white-space: nowrap;
}
.captcha-img-container img{
height: 44px;
border-radius: 4px;
}
.captcha-img-container a{
color: #1e88e5;
font-size: 13px;
}
/* radio 更靠近截图:一行内更紧凑 */
.layui-form-radio{ margin: 0 18px 0 0; }
/* 提交按钮居中、接近你导航蓝色 */
.contact-actions{ margin-top: 8px; }
.contact-actions .layui-btn{
height: 44px;
line-height: 44px;
padding: 0 34px;
border-radius: 4px;
background: #1e73b8;
font-weight: 700;
}
@media (max-width: 740px) {
.contact-card{ padding: 22px 16px 18px; }
.contact-title h2{ font-size: 28px; }
}
/* 只影响验证码这一行 */
.captcha-item .layui-input-group{
justify-content: flex-start; /* 不把验证码推到最右 */
}
/* 固定验证码输入框宽度(你可以改成 180/200/220 */
.captcha-item .layui-input-group > input.layui-input{
flex: 0 0 220px;
max-width: 220px;
}
/* 小屏幕下恢复全宽并换行更舒服 */
@media (max-width: 740px){
.captcha-item .layui-input-group{
flex-wrap: wrap;
gap: 10px;
}
.captcha-item .layui-input-group > input.layui-input{
flex: 1 1 100%;
max-width: 100%;
}
}
</style>
<body>
<div class="zh_content">
{include file="public/newnav" /}
</div>
<div class="layui-container contact-page">
<div class="contact-card">
<div class="contact-title">
<h2>{:lang('contact.menu_contact')}</h2>
</div>
<p class="contact-desc">
{:lang('contact.info')}
</p>
<div class="contact-hr"></div>
<form class="layui-form" id="Form1" lay-filter="Form1" autocomplete="off" style="display:block;">
<div class="layui-row layui-col-space20">
<div class="layui-col-xs12 layui-col-md12">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('contact.full_name')}<span>*</span></label>
<div class="layui-input-block">
<div class="layui-input-group">
<input type="text" name="name" value=""
placeholder="{:lang('contact.full_name')}"
lay-verify="required" class="layui-input">
</div>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('contact.email')}<span>*</span></label>
<div class="layui-input-block">
<div class="layui-input-group">
<input type="text" name="email" value=""
placeholder="{:lang('contact.email')}"
lay-verify="required|email" class="layui-input">
</div>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('contact.repeat_email')}<span>*</span></label>
<div class="layui-input-block">
<div class="layui-input-group">
<input type="text" name="re_email" value=""
placeholder="{:lang('contact.repeat_email')}"
lay-verify="required|email|sameEmail" class="layui-input">
</div>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('contact.passport_number')}<span>*</span></label>
<div class="layui-input-block">
<div class="layui-input-group">
<input type="text" name="passport_number" value=""
placeholder="{:lang('contact.passport_number')}"
lay-verify="required" class="layui-input">
</div>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('contact.is_apply')}<span>*</span></label>
<div class="layui-input-block">
<div class="layui-input-group" style="gap:22px;">
<input type="radio" name="is_apply" value="1" title="{:lang('contact.yes')}" alt="is_apply_1" lay-verify="otherReq"/>
<input type="radio" name="is_apply" value="0" title="{:lang('contact.no')}" alt="is_apply_0" lay-verify="otherReq"/>
</div>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-row data-name-is_apply data-is_apply_1">
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('contact.contact_reason')}<span>*</span></label>
<div class="layui-input-block">
<div class="layui-input-group">
<select name="reason_contact" class="layui-required">
<option value="">{:lang('contact.contact_reason')}</option>
{foreach $reason as $key=>$value}
<option value="{$value}">{$value}</option>
{/foreach}
</select>
</div>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('contact.order_sn')}<span style="color:#999;font-weight:600;">{:lang('contact.optional')}</span></label>
<div class="layui-input-block">
<div class="layui-input-group">
<input type="text" name="order_sn" value=""
placeholder="{:lang('contact.order_sn')}"
class="layui-input">
</div>
</div>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md12">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('contact.comment')}<span>*</span></label>
<div class="layui-input-block">
<div class="layui-input-group">
<textarea name="comments" class="layui-textarea" placeholder="{:lang('contact.comment')}" lay-verify="required"></textarea>
</div>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md12">
<div class="layui-form-item captcha-item">
<label class="layui-form-label">{:lang('contact.yzm')}<span>*</span></label>
<div class="layui-input-block">
<div class="layui-input-group">
<input type="text" name="yzm" value=""
placeholder="{:lang('contact.yzm')}"
lay-verify="required" class="layui-input">
<div class="layui-input-group-prepend">
<div class="captcha-img-container">
<img src="{:captcha_src()}" id="captcha-img"
onclick="refreshCaptcha()"
style="cursor:pointer; border:1px solid #ddd;">
<a href="javascript:void(0);" onclick="refreshCaptcha()">
{:lang('contact.change')}
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="layui-col-xs12 text-center contact-actions">
<div class="layui-form-item">
<button class="layui-btn" lay-submit lay-filter="formSubmit" id="submit">
{:lang('contact.submit_now')}
</button>
</div>
</div>
</div>
</form>
</div>
</div>
{include file="public/newfooter" /}
<script>
var layer = layui.layer, form = layui.form, laydate = layui.laydate, util = layui.util;
function refreshCaptcha() {
$('#captcha-img').attr('src', '{:captcha_src()}?t=' + Date.now());
}
form.on('submit(formSubmit)', function (data) {
var submitVal = "";
for (var i = 1; i < 2; i++) {
submitVal = $.extend(submitVal, form.val('Form' + i));
if (!form.validate('#Form' + i)) {
return false;
}
}
var load = layer.load(2, {shade: 0.3})
$.ajax({
url: "/home/contact/do_apply",
type: "POST",
data: submitVal,
success: function (data) {
layer.close(load);
refreshCaptcha()
if (data.code === 1) {
layer.msg(data.msg, {icon: 1}, function () {
window.location.reload()
});
} else {
layer.msg(data.msg, {icon: 5});
return false
}
}
});
return false;
});
form.verify({
/* 确认邮箱一致 */
sameEmail: function(value){
var email = $('input[name="email"]').val() || '';
if ((email.trim() !== (value || '').trim())) {
return '{:lang("contact.repeat_email")} 不一致';
}
},
otherReq: function (value, item) {
var $ = layui.$;
var verifyName = $(item).attr('name'),
verifyType = $(item).attr('type'),
formElem = $(item).parents('.layui-form'),
verifyElem = formElem.find('input[name=' + verifyName + ']'),
isTrue = verifyElem.is(':checked'),
focusElem = verifyElem.next().find('i.layui-icon'),
reqText = $(item).attr('lay-reqtext') || '{:lang("contact.default_reqtext")}';
if (!isTrue || !value) {
focusElem.css(verifyType == 'radio' ? {"color": "#FF5722"} : {"border-color": "#FF5722"});
focusElem.first().attr("tabIndex", "1").css("outline", "0").blur(function () {
focusElem.css(verifyType == 'radio' ? {"color": ""} : {"border-color": ""});
}).focus();
return reqText;
}
}
});
$("input[type='radio']").each(function () {
var alt = $(this).attr("alt");
if ($(this).is(":checked")) {
$(".data-" + alt).show();
} else {
$(".data-" + alt).hide();
}
})
form.on("radio", function (data) {
var name = data.elem.name;
var alt = data.elem.alt;
var $sections = $(".data-name-" + name);
$sections.each(function () {
var $section = $(this);
var isMatch = $section.hasClass("data-" + alt);
if (isMatch) {
$section.show();
} else {
$section.hide();
// 隐藏时移除 required 相关验证(你原逻辑保留)
$section.find("input, textarea, select").each(function () {
var $el = $(this);
var old = $el.attr("lay-verify") || "";
var newVerify = old
.split("|")
.filter(v => !["required", "otherReq", "checkboxChecked"].includes(v))
.join("|");
$el.attr("lay-verify", newVerify);
});
$section.find(".xm-select-default").each(function () {
var $el = $(this);
var old = $el.attr("lay-verify") || "";
var newVerify = old
.split("|")
.filter(v => !["required", "otherReq", "checkboxChecked"].includes(v))
.join("|");
$el.attr("lay-verify", newVerify);
});
}
});
// 只给当前显示块补 required
var $target = $(".data-" + alt);
$target.children().find(".layui-required").each(function () {
const $el = $(this);
const isVisible = $el.closest(".layui-form-item, .layui-col-xs12, .layui-col-md6").is(":visible");
if (!isVisible) return;
let verifyList = ($el.attr("lay-verify") || "").split("|").filter(Boolean);
if ($el.is(":radio")) {
if (!verifyList.includes("otherReq")) verifyList.push("otherReq");
} else if ($el.is(":checkbox")) {
if (!verifyList.includes("checkboxChecked")) verifyList.push("checkboxChecked");
} else {
if (!verifyList.includes("required")) verifyList.push("required");
}
$el.attr("lay-verify", verifyList.join("|"));
});
$target.find(".xm-select-default:visible").each(function () {
var $el = $(this);
var verifyList = ($el.attr("lay-verify") || "").split("|").filter(Boolean);
if (!verifyList.includes("required")) verifyList.push("required");
$el.attr("lay-verify", verifyList.join("|"));
});
form.render("radio");
form.render("checkbox");
});
</script>
</body>

View File

@@ -0,0 +1,279 @@
{include file="public/head" /}
<link rel="stylesheet" href="/static/css/index.css">
<style>
body {
background: #fff !important;
}
</style>
<body>
{include file="public/newnav" /}
<!-- ===== Hero ===== -->
<section class="hero">
<div class="container">
<div class="hero-card">
<h1>{$country|default=''}{:lang('index.visa_service')}</h1>
<p>
{:str_replace('%country%', $country ?? '', lang('index.welcome_message'))} </p>
<a class="btn btn-primary btn-primary-brand w-100" href="/home/visa/index.html">{:lang('index.online_application')}</a>
</div>
</div>
</section>
<!-- ===== Notice ===== -->
<section class="notice">
<div class="container">
<p>
<span class="tip-title">{:lang('index.notice')}</span>
{:lang('index.notice_text')}
</p>
</div>
</section>
<!-- ===== Flow ===== -->
<section class="section">
<div class="container">
<div class="section-title">{:lang('index.process_title')}</div>
<div class="row g-3 align-items-stretch">
<div class="col-lg-7">
<div class="flow2">
<div class="step">
<div class="badge-num">1</div>
<div>
<h6>{:lang('index.step1_title')}</h6>
<p>{:lang('index.step1_desc')}</p>
</div>
</div>
<div class="step">
<div class="badge-num">2</div>
<div>
<h6>{:lang('index.step2_title')}</h6>
<p>{:lang('index.step2_desc')}</p>
</div>
</div>
<div class="step">
<div class="badge-num">3</div>
<div>
<h6>{:lang('index.step3_title')}</h6>
<p>{:lang('index.step3_desc')}</p>
</div>
</div>
</div>
</div>
<div class="col-lg-5">
<div class="flow2-side">
<div class="k"><i class="bi bi-lightning-charge me-1"></i>{:lang('index.tip')}</div>
<div class="i">
• {:lang('index.tip_1')}<br>
• {:lang('index.tip_2')}<br>
• {:lang('index.tip_3')}<br>
• {:lang('index.tip_4')}
</div>
<div class="d-grid mt-3">
<a class="btn btn-primary btn-primary-brand" href="/home/visa/index.html">{:lang('index.online_application')}</a>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ===== Intro + Map ===== -->
<section class="section pt-0">
<div class="container">
<div class="intro2" id="apply">
<div class="row g-0">
<div class="col-lg-5">
<div class="left"></div>
</div>
<div class="col-lg-7">
<div class="right">
<h2>{$country|default=''}{:lang('index.entry_card_application')}</h2>
<p style="color: rgba(15,23,42,.72); line-height:1.9; margin:0;">
{:str_replace('%country%', $country ?? '', lang('index.application_description1'))}
</p>
<div class="bul">
<div><i class="bi bi-check2-circle"></i> {:str_replace('%country%', $country ?? '', lang('index.application_notice'))}</div>
</div>
<a class="btn btn-outline-brand w-100" href="/home/visa/index.html">
{$country|default=''}{:lang('index.apply_now')}
</a>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ===== 为什么选择我们 ===== -->
<section class="section pt-0">
<div class="container">
<div class="why-title"> {:lang('index.why_choose_us_title')}</div>
<div class="why-sub"> {:lang('index.why_choose_us_subtitle')}</div>
<div class="row g-3">
<div class="col-lg-6">
<div class="why2-item">
<div class="why2-ico"><i class="bi bi-lightning-charge"></i></div>
<div>
<h6>{:lang('index.convenient')}</h6>
<p>{:lang('index.convenient_desc')}</p>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="why2-item">
<div class="why2-ico"><i class="bi bi-shield-check"></i></div>
<div>
<h6>{:lang('index.secure')}</h6>
<p>{:lang('index.secure_desc')}</p>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="why2-item">
<div class="why2-ico"><i class="bi bi-award"></i></div>
<div>
<h6>{:lang('index.approval')}</h6>
<p>{:lang('index.approval_desc')}</p>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="why2-item">
<div class="why2-ico"><i class="bi bi-headset"></i></div>
<div>
<h6>{:lang('index.support')}</h6>
<p>{:lang('index.support_desc')}</p>
</div>
</div>
</div>
</div>
<div class="text-center mt-4">
<a class="btn btn-primary btn-primary-brand px-5" href="/home/visa/index.html">
{$country|default=''}{:lang('index.apply_now')}
</a>
</div>
</div>
</section>
<section class="section pt-0" id="plans">
<div class="container">
<div class="section-title">{:lang('index.setmeal')}</div>
<div class="row g-3">
<div class="col-lg-4">
<div class="plan">
<span class="tag">{:lang('index.setmeal_st')}</span>
<h5>{:lang('index.suitable_for_advance_preparation')}</h5>
<div class="meta">{:lang('index.processing_time_limit')}</div>
<ul>
<li>{:lang('index.chinese_guided_filling')}</li>
<li>{:lang('index.key_field_consistency_check')}</li>
<li>{:lang('index.basic_error_reminder')}</li>
</ul>
<div class="d-grid mt-3">
<a class="btn btn-outline-brand" href="/home/visa/index.html">{:lang('index.select_standard_plan')}</a>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="plan" style="border-color: rgba(240,90,40,.35);">
<span class="tag" style="border-color: rgba(240,90,40,.22); background: rgba(240,90,40,.10);">{:lang('index.setmeal_ex')}</span>
<h5>{:lang('index.suitable_for_departure_soon')}</h5>
<div class="meta">{:lang('index.urgent_availability')}</div>
<ul>
<li>{:lang('index.priority_processing_queue')}</li>
<li>{:lang('index.stricter_reminder_for_key_fields')}</li>
<li>{:lang('index.faster_feedback_and_supplementary_suggestions')}</li>
</ul>
<div class="d-grid mt-3">
<a class="btn btn-accent" href="/home/visa/index.html">{:lang('index.select_urgent_plan')}</a>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="plan">
<span class="tag">{:lang('index.setmeal_team')}</span>
<h5>{:lang('index.group_travel_batch_reg')}</h5>
<div class="meta">{:lang('index.group_meta_info')}</div>
<ul>
<li>{:lang('index.batch_data_collect')}</li>
<li>{:lang('index.unified_verify_submit')}</li>
<li>{:lang('index.progress_track_reconcile')}</li>
</ul>
<div class="d-grid mt-3">
<a class="btn btn-primary btn-primary-brand" href="/home/contact">{:lang('index.contact_team_plan')}</a>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ===== 新增模块 2FAQ提升转化不改整体风格 ===== -->
<section class="section pt-0" id="faq">
<div class="container">
<div class="section-title">{:lang('index.common_questions')}</div>
<div class="accordion" id="accFaq">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#f1">
{:lang('index.faq_is_official_website')}
</button>
</h2>
<div id="f1" class="accordion-collapse collapse" data-bs-parent="#accFaq">
<div class="accordion-body" style="color: rgba(15,23,42,.72); line-height: 1.9; font-size: 13px;">
{:lang('index.faq_is_official_website_answer')}
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#f2">
{:lang('index.faq_guarantee_approval')}
</button>
</h2>
<div id="f2" class="accordion-collapse collapse" data-bs-parent="#accFaq">
<div class="accordion-body" style="color: rgba(15,23,42,.72); line-height: 1.9; font-size: 13px;">
{:lang('index.faq_guarantee_approval_answer')}
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#f3">
{:lang('index.faq_query_download_after_payment')}
</button>
</h2>
<div id="f3" class="accordion-collapse collapse" data-bs-parent="#accFaq">
<div class="accordion-body" style="color: rgba(15,23,42,.72); line-height: 1.9; font-size: 13px;">
{:lang('index.faq_query_download_after_payment_answer')}
</div>
</div>
</div>
</div>
</div>
</section>
{include file="public/newfooter" /}
</body>
</html>

View File

@@ -0,0 +1,187 @@
{include file="public/head"}
<link rel="stylesheet" href="/static/css/index.css?v={:time()}">
<style>
body {
background: #fff !important;
}
</style>
<body>
<div class="zh_content">
{include file="public/nav"}
<div class="swiper-wrapper">
<div class="swiper-slide">
<div class="swiper-slide-img">
</div>
</div>
</div>
<div class="evus-main">
<div class="djcon">
<div class="dj-box">
<div class="dj-txt" style="font-size: 24px; line-height: 40px; color: #14286d;">{$country|default=''}{:lang('index.visa_service')}</div>
<div class="dj-txt2">{:str_replace('%country%', $country ?? '', lang('index.welcome_message'))}</div>
<a href="/home/visa/index.html" class="dj-btn">{:lang('index.online_application')}</a>
</div>
</div>
</div>
</div>
<div class="container-apply notice" style="margin-top: 16px">
<p style="font-size: 16px;line-height: 30px;">
<span style="color: red;font-weight: bold;">{:lang('index.notice')}</span>
<span>{:lang('index.notice_text')}</span>
</p>
</div>
<div class="process">
<div class="ps-tit">{:lang('index.process_title')}</div>
<div class="pro-con">
<div class="ps-item item-border">
<img src="/static/image/icon-infor.png" alt="">
<div class="ps-text">{:lang('index.step1_title')}</div>
<div class="ps-text">{:lang('index.step1_desc')}</div>
</div>
<div class="ps-item ps-item2">
<img class="pc-ps-item" src="/static/image/icon-arrow.png" alt="">
<img style="display: none" class="m-ps-item" src="/static/image/micon-arrow.png" alt=""/>
</div>
<div class="ps-item item-border">
<img src="/static/image/icon-up2.png" alt="">
<div class="ps-text">{:lang('index.step2_title')}</div>
<div class="ps-text">{:lang('index.step2_desc')}</div>
</div>
<div class="ps-item ps-item2">
<img class="pc-ps-item" src="/static/image/icon-arrow.png" alt="">
<img style="display: none" class="m-ps-item" src="/static/image/micon-arrow.png" alt=""/>
</div>
<div class="ps-item item-border">
<img src="/static/image/icon-down.png" alt="">
<div class="ps-text">{:lang('index.step3_title')}</div>
<div class="ps-text">{:lang('index.step3_desc')}</div>
</div>
</div>
</div>
<div class="container-apply">
<div class="apply-item">
<img src="/static/image/malaixiya.jpg" >
</div>
<div class="apply-item" style="margin-top:35px;">
<div class="bt" style="text-align: center">{$country|default=''}{:lang('index.entry_card_application')}</div>
<div style="font-size:16px; line-height:30px;">
{:str_replace('%country%', $country ?? '', lang('index.application_description1'))}
<br>
<p style="padding-top:15px;line-height:30px;">
{:str_replace('%country%', $country ?? '', lang('index.application_notice'))}
</p>
<div style="text-align: center;">
<a href="{:url('visa/index')}">
<button class="dj-btn">{$country|default=''}{:lang('index.apply_now')}</button>
</a>
</div>
</div>
</div>
</div>
{include file="public/middle"}
<div style="text-align: center;margin-bottom: 20px;">
<a href="{:url('visa/index')}">
<button class="dj-btn">{$country|default=''}{:lang('index.apply_now')}</button>
</a>
</div>
<div class="advantages-section">
<div class="content1">
<h3 class="title">{$country|default=''}{:lang('index.electronic_arrival_card')}</h3>
<div style="color:#fff; font-size:16px; line-height:35px;">
{:str_replace('%country%', $country ?? '', lang('index.environmental_initiative'))}<br>
{:lang('index.digital_process_benefits')}<br><br>
{:str_replace('%country%', $country ?? '', lang('index.submission_requirement'))}<br>
{:str_replace('%country%', $country ?? '', lang('index.card_validity'))}
</div>
<div style="text-align: center;margin-bottom: 20px;">
<a href="{:url('visa/index')}">
<button class="dj-btn" style="background: linear-gradient(180deg, #a83723 0, #a83723 100%);">{$country|default=''}{:lang('index.apply_now')}</button>
</a>
</div>
</div>
</div>
{include file="public/footer"}
</body>
<script>
// 监听复选框点击
$('.termsCheckbox').click(function (e) {
e.preventDefault();
if (window.innerWidth <= 768) {
area = ['90%', '90%']
} else {
area = ['50%', '60%']
}
// 弹出条款内容的弹窗
layer.open({
type: 1, // 弹窗类型:页面层
title: '{:lang(\'js.terms_conditions\')}', content: `
<div style="padding: 20px;">
<h3>{:lang(\'js.terms_conditions\')}</h3>
<p></p><p><span style="font-size: 12px;">{:str_replace(\'%country%\', $country ?? \'\', lang(\'js.terms_intro\'))}</span></p>
<p><span style="font-size: 12px;">{:str_replace(\'%country%\', $country ?? \'\', lang(\'js.our_services\'))}</span></p>
<p><span style="font-size: 12px;">{:str_replace(\'%country%\', $country ?? \'\', lang(\'js.application_process\'))}</span></p>
<p><span style="font-size: 12px;">{:lang(\'js.read_confirmation\')}</span></p>
<p><br></p><p></p>
</div>
`, area: area, // 弹窗大小
btn: ['关闭'], // 按钮
yes: function (index) {
layer.close(index); // 关闭弹窗
}
});
});
$('.privacy_policy').click(function (e) {
e.preventDefault();
if (window.innerWidth <= 768) {
area = ['90%', '90%']
} else {
area = ['50%', '60%']
}
// 弹出条款内容的弹窗
layer.open({
type: 1, // 弹窗类型:页面层
title: '{:lang(\'js.privacy_policy\')}', content: `
<div style="padding: 20px;">
<h3>{:lang(\'js.privacy_policy\')}</h3>
<p></p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.privacy_intro\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">1. {:lang(\'js.scope\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.scope_content\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">2. {:lang(\'js.information_use\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.information_use_content\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">3. {:lang(\'js.information_disclosure\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.information_disclosure_content\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">4. {:lang(\'js.information_storage\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.information_storage_content\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">5. {:lang(\'js.cookie_use\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.cookie_use_content\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">6. {:lang(\'js.information_security\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.information_security_content\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">7. {:lang(\'js.policy_changes\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.policy_changes_content\')}</p>
<p><br></p><p></p>
</div>
`, area: area, // 弹窗大小
btn: ['关闭'], // 按钮
yes: function (index) {
layer.close(index); // 关闭弹窗
}
});
});
</script>
</html>

View File

@@ -0,0 +1,182 @@
{include file="public/head" /}
<link rel="stylesheet" href="/static/css/user.css?v={:time()}">
<link rel="stylesheet" href="/static/css/invoice.css?v={:time()}">
</head>
<body class="user-page">
{include file="public/newnav" /}
<main class="user-main">
<div class="layui-container">
<div class="account-layout">
<aside class="account-nav">
<div class="account-nav__title">个人中心</div>
<a href="{:url('user/index')}" class="account-nav__link">
<i class="bi bi-receipt"></i><span>我的订单</span>
</a>
<a href="{:url('user/invoice')}" class="account-nav__link is-active">
<i class="bi bi-file-earmark-text"></i><span>自助开票</span>
</a>
<a href="{:url('user/multiple')}" class="account-nav__link">
<i class="bi bi-files"></i><span>合并开票</span>
</a>
</aside>
<section class="account-panel">
<div class="account-panel__title">发票申请</div>
<div class="account-panel__body">
<div class="invoice-summary">
<div>
<strong>开票金额</strong>
<span>{$data.total_price}</span>
</div>
<p>提交后,商家将在 1-3 个工作日内完成开票。</p>
</div>
<p class="invoice-tip">
个人开票时,税号请填写身份证号码;企业开票请填写统一社会信用代码。
</p>
<form class="layui-form invoice-form" action="">
<div class="invoice-section-title">必填信息</div>
<div class="layui-row layui-col-space24">
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">手机号<span>*</span></label>
<div class="layui-input-block">
<input type="tel" autocomplete="off" class="layui-input" lay-verify="required|phone" name="phone" placeholder="用于接收发票通知">
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">客户名称<span>*</span></label>
<div class="layui-input-block">
<input type="text" name="title" lay-verify="required" placeholder="限55个字符" maxlength="55" autocomplete="off" class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">税号<span>*</span></label>
<div class="layui-input-block">
<input type="text" name="tax_number" lay-verify="required" placeholder="请输入税号或身份证号码" autocomplete="off" class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">联系人<span>*</span></label>
<div class="layui-input-block">
<input type="text" name="link_person" lay-verify="required" placeholder="请输入联系人姓名" autocomplete="off" class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12">
<div class="layui-form-item">
<label class="layui-form-label">邮箱<span>*</span></label>
<div class="layui-input-block">
<input type="email" name="link_email" lay-verify="required|email" placeholder="用于接收电子发票" autocomplete="off" class="layui-input">
</div>
</div>
</div>
</div>
<div class="invoice-section-title invoice-section-title--sub">补充信息</div>
<div class="layui-row layui-col-space24">
<div class="layui-col-xs12">
<div class="layui-form-item">
<label class="layui-form-label">地址</label>
<div class="layui-input-block">
<input type="text" name="address" placeholder="请输入注册地址" autocomplete="off" class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">电话</label>
<div class="layui-input-block">
<input type="text" name="mobile" placeholder="请输入联系电话" autocomplete="off" class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">开户行</label>
<div class="layui-input-block">
<input type="text" name="bank" placeholder="请输入开户银行" autocomplete="off" class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12">
<div class="layui-form-item">
<label class="layui-form-label">账号</label>
<div class="layui-input-block">
<input type="text" name="account" placeholder="请输入银行账号" autocomplete="off" class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12">
<input type="hidden" name="order_id" value="{$data.id}">
<input type="hidden" name="order_sn" value="{$data.order_sn}">
<button type="submit" id="button" class="layui-btn layui-btn-fluid" lay-submit lay-filter="demo1">
<span>提交申请</span>
</button>
</div>
</div>
</form>
</div>
</section>
</div>
</div>
</main>
{include file="public/newfooter" /}
<!--//倒计时-->
<script>
layui.use(function(){
var $ = layui.$;
var form = layui.form;
var layer = layui.layer;
// 提交事件
form.on('submit(demo1)', function(data){
var field = data.field; // 获取表单字段值
field.tax_number = $.trim(field.tax_number || '');
var tax_len = field.tax_number.length;
var arr = [15, 17, 18, 20];
if (arr.indexOf(tax_len) == -1) {
layer.msg('仅支持购方税号长度为15、17、18、20请重新输入');
return false;
}
var $button = $('#button');
var buttonText = $button.find('span').text();
$button.prop('disabled', true).find('span').text('提交中...');
$.ajax({
url: "/home/invoice/add_invoice",
type: 'POST',
data: field,
success: function (res) {
if (res.status == 1) {
layer.alert('已提交开票申请,商家会在 1-3个工作日内完成开票,<br>请扫描下方二维码进行添加客服收取发票<br><div class="in-img"><img src="{$fapiao_kefu}" width: 150px;></div>', {
icon: 1,
title:'发票申请',
closeBtn: 0
}, function(index){
layer.close(index);
window.location.href = res.url;
});
} else {
$button.prop('disabled', false).find('span').text(buttonText);
layer.msg(res.msg || '提交失败,请稍后重试');
}
},
error: function () {
$button.prop('disabled', false).find('span').text(buttonText);
layer.msg('网络异常,请稍后重试');
}
})
return false; // 阻止默认 form 跳转
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,591 @@
{include file="public/head"}
<style>
/* ---- Card ---- */
.lookup-card{
background:#fff;
border:1px solid rgba(0,0,0,.06);
box-shadow: 0 2px 10px rgba(0,0,0,.06);
border-radius: 6px;
margin: 18px 0 26px;
overflow:hidden;
max-width: 100%;
}
.lookup-bar{
background: var(--main-color);
color:#fff;
font-size: 24px;
font-weight: 900;
padding: 8px 16px;
line-height: 1.2;
}
.lookup-body{
padding: 18px 22px 8px;
}
/* ---- Form labels above ---- */
.label-above .layui-form-label{
display:block;
float:none;
width:auto !important;
text-align:left;
padding: 0 0 6px 0;
font-weight: 800;
font-size: 18px; /* PC 默认就用 18避免过大 */
color:#3a3a3a;
}
.label-above .layui-form-label span{
color:#e53935;
margin-left:4px;
font-weight:900;
}
.layui-input-block{
margin-left:0;
min-height: auto;
box-sizing: border-box;
}
/* ---- Inputs ---- */
.layui-form input.layui-input{
height: 50px; /* 统一高度 */
border: 1px solid #d6d6d6;
border-radius: 2px;
padding: 10px 12px;
font-size: 14px; /* PC 默认 14 */
color:#333;
background:#fff;
box-sizing: border-box;
}
/* Layui date input background keep */
.layui-input-wrap .layui-input-prefix + .layui-input,
.layui-input-wrap .layui-input-prefix ~ * .layui-input{
background:#fff;
}
/* ---- Date prefix icon vertical align ---- */
.layui-input-wrap{
display: flex;
align-items: center; /* 关键prefix 与 input 垂直居中 */
}
.layui-input-wrap .layui-input-prefix{
display: flex;
align-items: center;
justify-content: center;
height: 50px;
line-height: 50px;
}
/* ---- Help tip ---- */
.help-tip{
margin-top: 8px;
display:flex;
align-items:flex-start;
gap: 6px;
color:#666;
font-size: 13px;
line-height: 1.6;
}
.help-tip i{
font-size: 14px;
color:#333;
margin-top: 2px;
}
/* ---- Button ---- */
.btn-row{ margin-top: 8px; }
.layui-btn{
background:#153854;
border:1px solid #153854;
box-shadow: 0 2px 4px rgba(0,0,0,.35);
border-radius: 6px;
font-size: 20px;
font-weight: 900;
height: 50px;
line-height: 50px;
padding: 0 28px;
letter-spacing: .5px;
box-sizing: border-box;
}
.btn-fixed{
width: 520px;
max-width: 100%;
}
/* ---- Result area ---- */
#result{ padding: 0 22px 22px; }
.container-tit{
font-size: 18px;
font-weight: 900;
color:#153854;
margin: 12px 0 8px;
}
.container-con{
font-size: 14px;
color:#555;
margin-bottom: 12px;
}
.layui-table thead{
color:#fff;
background:#153854;
}
.layui-laydate .layui-this,
.layui-laydate .layui-this > div{
background-color:#153854 !important;
color:#fff !important;
}
/* ---- Prevent overflow caused by row negative margin / fixed width elements ---- */
.layui-row{
max-width: 100%;
box-sizing: border-box;
}
img, table, .layui-table-view{
max-width: 100% !important;
}
/* ===============================
Mobile adjustments
=============================== */
@media (max-width: 768px){
/* 容器左右留白,防止撑破 */
.layui-container{
padding-left: 12px !important;
padding-right: 12px !important;
}
/* row 可能有负 margin清掉 */
.layui-row{
margin-left: 0 !important;
margin-right: 0 !important;
}
/* 卡片内部 padding 更紧凑 */
.lookup-bar{
font-size: 22px;
padding: 12px 14px;
}
.lookup-body{
padding: 14px 14px 6px;
}
.label-above .layui-form-label{
font-size: 18px;
}
.layui-form input.layui-input{
height: 50px;
font-size: 15px;
}
.layui-input-wrap .layui-input-prefix{
height: 50px;
line-height: 50px;
}
.layui-btn{
font-size: 22px;
height: 62px;
line-height: 62px;
width: 100%;
}
.layui-table-header{ display:none; }
}
/* ===============================
Desktop fine-tune (optional)
=============================== */
@media (min-width: 992px){
/* 大屏让卡片更像截图:稍微增加左右 padding */
.lookup-body{ padding: 18px 22px 8px; }
}
</style>
<body>
<div class="zh_content">
{include file="public/newnav" }
</div>
<div class="layui-container">
<div class="lookup-card">
<div class="lookup-bar">{:lang('lookup.status_query_title')}</div>
<div class="lookup-body">
<form class="layui-form label-above" lay-filter="Form1" id="Form1" autocomplete="off">
<div class="layui-row layui-col-space30" style="margin-top: 10px;">
<!-- 护照号 -->
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label" for="passport_number">
{:lang('lookup.passport_number')}<span>*</span>
</label>
<div class="layui-input-block passport-wrap">
<input type="text" name="passport_number" lay-verify="required"
placeholder="{:lang('lookup.passport_number_placeholder')}"
class="layui-input" value="">
<!-- 关键:给 file 一个 id并隐藏 -->
<input id="passport_file" class="passport-file" type="file" accept="image/*"
onchange="uploadPassport(this,'passport')">
<!-- 关键label 的 for 指向 file 的 id -->
<label class="passport-upload" for="passport_file" title="拍照/上传有效护照照片">
<i class="layui-icon layui-icon-camera"></i>
</label>
</div>
</div>
</div>
<!-- 出生日期 -->
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label" for="birth_date">
{:lang('lookup.birth_date')}<span>*</span>
</label>
<div class="layui-input-block">
<div class="layui-input-wrap">
<div class="layui-input-prefix">
<i class="layui-icon layui-icon-date"></i>
</div>
<input type="text" name="birth_date" id="birth_date" lay-verify="required"
placeholder="{:lang('lookup.birth_date_placeholder')}"
class="layui-input layui-time" value="">
</div>
</div>
</div>
</div>
<!---->
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('lookup.last_name')}<span>*</span></label>
<div class="layui-input-block">
<input type="text" name="last_name" lay-verify="required|english"
placeholder="{:lang('lookup.last_name_placeholder')}"
class="layui-input" value="">
<div class="help-tip">
<i class="layui-icon layui-icon-tips-fill"></i>
<span>{:lang('lookup.last_name_tooltip')}</span>
</div>
</div>
</div>
</div>
<!---->
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('lookup.first_name')}<span>*</span></label>
<div class="layui-input-block">
<input type="text" name="first_name" lay-verify="required|english"
placeholder="{:lang('lookup.first_name_placeholder')}"
class="layui-input" value="">
<div class="help-tip">
<i class="layui-icon layui-icon-tips-fill"></i>
<span>{:lang('lookup.first_name_tooltip')}</span>
</div>
</div>
</div>
</div>
<!-- 按钮:半宽且不铺满 -->
<div class="layui-col-xs12 layui-col-md6 btn-row">
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn btn-fixed" lay-submit lay-filter="formSubmit" id="submit">
{:lang('lookup.query_status_button')}
</button>
</div>
</div>
</div>
</div>
</form>
</div>
<div class="layui-row container" style="display:none" id="result">
<div class="layui-col-xs12 layui-col-md12" style="display:none" id="result1">
<div class="container-tit">{:lang('lookup.registered_status')}</div>
<div class="container-con">{:lang('lookup.approval_message')}</div>
</div>
<div class="layui-col-xs12 layui-col-md12">
<table class="layui-hide" id="demo"></table>
</div>
</div>
</div>
</div>
{include file="public/newfooter" }
<script>
var layer = layui.layer, form = layui.form, laydate = layui.laydate, table = layui.table;
var ocrConfigRaw = '{$ocr_config|default = ''}'.replace(/&quot;/g, '"');
var ocrConfig = {};
var sdk = null;
try {
ocrConfig = ocrConfigRaw ? JSON.parse(ocrConfigRaw) : {};
} catch (e) {
console.error('Failed to parse OCR config', e);
}
if (window.PassportOCRSDK && ocrConfig.base_url && ocrConfig.app_id && ocrConfig.signature && ocrConfig.timestamp) {
try {
sdk = PassportOCRSDK.create({
apiBase: ocrConfig.base_url,
appId: ocrConfig.app_id,
sign: ocrConfig
});
} catch (e) {
console.error('Failed to init OCR sdk', e);
}
}
// 更稳的手机判断
function isMobile() {
return window.matchMedia && window.matchMedia('(max-width: 767.98px)').matches;
}
// 日期控件(建议固定格式)
$(".layui-form-item").find(".layui-time").each(function () {
laydate.render({
elem: this,
trigger: 'click',
format: 'yyyy-MM-dd'
});
});
// ✅兼容你 onchange 传了第二个参数
function uploadPassport(_this, type) {
var file = _this.files && _this.files[0];
var loadingIndex = null;
var closeLoading = function () {
if (loadingIndex !== null) {
layer.close(loadingIndex);
loadingIndex = null;
}
};
var resetFileInput = function () {
$(_this).val('');
};
if (!file) {
layer.msg('{:lang("controller.upload_missing")}', {icon: 5});
return false;
}
if (!/image\/\w+/.test(file.type || '')) {
layer.msg("{:lang('js.upload_image_only')}", {icon: 5});
return false;
}
if (!FileReader) {
layer.msg("{:lang('js.browser_not_supported')}", {icon: 5});
return false;
}
var url = "/home/upload/upload_passport";
loadingIndex = layer.load(2, {shade: 0.3});
compressImage(file, 0.2, async function (compressedFile) {
try {
if (!compressedFile) {
closeLoading();
resetFileInput();
layer.msg('{:lang("js.upload_retry")}', {icon: 5});
return false;
}
var ocrWarningMsg = '';
if (sdk) {
try {
var ocrData = await sdk.recognizePassport(compressedFile, {
auto_rotate: true,
return_debug: false
});
if (ocrData && ocrData.code === 1 && ocrData.data) {
var ocr = ocrData.data || {};
var pad2 = function (n) {
return String(n || '').padStart(2, '0');
};
var buildDate = function (y, m, d) {
if (!y || !m || !d) return '';
return y + '-' + pad2(m) + '-' + pad2(d);
};
$('input[name="last_name"]').val(ocr.last_name || '');
$('input[name="first_name"]').val(ocr.first_name || '');
$('input[name="passport_number"]').val(ocr.passport_number || '');
var birthDate = buildDate(ocr.birth_date_year, ocr.birth_date_month, ocr.birth_date_day);
if (birthDate) {
$('input[name="birth_date"]').val(birthDate);
}
} else {
ocrWarningMsg = '{:lang("js.manual_entry_required")}';
}
} catch (ocrErr) {
ocrWarningMsg = '{:lang("js.manual_entry_required")}';
}
} else {
ocrWarningMsg = '{:lang("js.manual_entry_required")}';
}
var formData = new FormData();
formData.append('file', compressedFile);
formData.append('path', 'mala');
$.ajax({
url: url,
type: "post",
data: formData,
dataType: "json",
contentType: false,
processData: false,
mimeType: "multipart/form-data",
success: function (res) {
closeLoading();
resetFileInput();
if (res.code != 1) {
//layer.msg('{:lang("js.manual_entry_required")}', {icon: 5});
//return;
}
},
error: function () {
closeLoading();
resetFileInput();
//layer.msg('{:lang("js.manual_entry_required")}', {icon: 5});
}
});
if (ocrWarningMsg) {
layer.msg(ocrWarningMsg, {icon: 5});
}
} catch (e) {
closeLoading();
resetFileInput();
layer.msg('{:lang("js.manual_entry_required")}', {icon: 5});
}
});
}
function compressImage(file, quality, callback) {
console.log("开始压缩"+Date.now())
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function (event) {
const img = new Image();
img.src = event.target.result;
img.onload = function () {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
let maxWidth = 1200;
let width = img.width;
let height = img.height;
if (width > maxWidth) {
height *= maxWidth / width;
width = maxWidth;
}
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0, width, height);
canvas.toBlob(function (blob) {
const newFile = new File([blob], file.name, { type: file.type });
callback(newFile);
}, file.type, quality);
};
};
}
// 监听提交
form.on('submit(formSubmit)', function (data) {
$('#result').hide();
$('#result1').hide();
$.ajax({
type: "POST",
url: "/home/lookup/enrollment",
data: data.field,
beforeSend: function () {
window.index1 = layer.load(1, {shade: [0.3, '#fff']});
},
success: function (_data) {
layer.close(window.index1);
if (_data.code == 0) {
layer.msg(_data.msg, {icon: 5});
return;
}
$('#result').show();
var row = _data.data || {};
var hasFile = !!(row.file_url && row.file_url !== '');
if (isMobile()) {
// 手机:字段-值
var mobileData = [
{field: '{:lang("lookup.passport_number")}', value: row.passport_number || ''},
{field: '{:lang("lookup.birth_date")}', value: row.birth_date || ''},
{field: '{:lang("lookup.last_name")}', value: row.last_name || ''},
{field: '{:lang("lookup.first_name")}', value: row.first_name || ''},
{field: hasFile ? '{:lang("lookup.document")}' : '{:lang("lookup.status")}', value: row.file_url || ''}
];
if (hasFile) $('#result1').show();
table.render({
elem: '#demo',
data: mobileData,
cols: [[
{field:'field', title:'{:lang("lookup.field")}', width:110},
{
field:'value',
title:'{:lang("lookup.value")}',
templet: function(d){
if (d.field === '{:lang("lookup.document")}') {
return '<a href="'+ row.file_url +'?'+ Math.random() +'" target="_blank"><img src="/static/image/pdf.png" alt="{:lang("lookup.click_download")}"></a>';
}
if (d.field === '{:lang("lookup.status")}') {
return '{:lang("lookup.in_progress")}';
}
return d.value;
}
}
]]
});
} else {
if (hasFile) $('#result1').show();
table.render({
elem: '#demo',
data: [row],
cols: [[
{field:'last_name', title:'{:lang("lookup.last_name")}', width:200},
{field:'first_name', title:'{:lang("lookup.first_name")}', width:200},
{field:'birth_date', title:'{:lang("lookup.birth_date")}', width:200},
{field:'passport_number', title:'{:lang("lookup.passport_number")}'},
{
field:'file_url',
title: hasFile ? '{:lang("lookup.document")}' : '{:lang("lookup.status")}',
templet: function(){
if (hasFile) {
return '<a href="'+ row.file_url +'?'+ Math.random() +'" target="_blank"><img src="/static/image/pdf.png" alt="{:lang("lookup.click_download")}"></a>';
}
return '{:lang("lookup.in_progress")}';
}
}
]]
});
}
}
});
return false;
});
form.verify({
english: [/^[A-Za-z]+$/, '{:lang("lookup.english_only")}']
});
</script>
</body>
</html>

View File

@@ -0,0 +1,141 @@
{include file="public/head"}
</head>
<style>
@media (max-width: 1110px) {
.nav a {
width: 35%;
}
}
</style>
<style>
.news_cc {
width: 100%;
}
a:link, a:visited {
text-decoration: none;
}
.news_item {
width: 100%;
height: auto;
overflow: hidden;
margin-top: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
cursor: pointer;
display: block;
}
.news_intro p,.news_intro span{
width: 100%;
line-height: 24px !important;
font-size: 13px !important;
color: #888 !important;
margin: 0 !important;
}
a {
cursor: pointer;
color: #333;
}
.news_text {
float: left;
width: 100%;
}
span{
font-style:unset !important;
}
.news_ct {
width: 100%;
line-height: 18px;
font-size: 15px;
margin-bottom: 8px;
}
.news_intro {
width: 100%;
line-height: 24px;
font-size: 13px;
color: #888;
overflow: hidden;
}
.news_time {
text-align: right;
color: #888;
line-height: 28px;
font-size: 12px;
}
@media (max-width: 720px){
.content {
width: 100%;
padding: 30px 0px 0px;
background: none;
}
}
.layui-laypage .layui-laypage-curr .layui-laypage-em {
background-color: var(--main-color);
}
</style>
<body style="background: #F8F8F8;display: flex; flex-direction: column; min-height: 100vh;">
<div class="zh_content">
{include file="public/newnav" }
</div>
<div class="layui-container" style="flex: 1;margin-top: 36px;">
<div class="modular-row callout bg-grey">
<div class="container">
<div class="news_cc">
{volist name="news_list" id="vo"}
<a class="news_item" href="{:url('news/detail', ['id' => $vo.id])}">
<div class="news_text">
<div class="news_ct animate_tit">{:cookie('think_var') == 'en-us' ? $vo['en_title'] : $vo['title']}</div>
<div class="news_intro">
<?= mb_strlen($plain = strip_tags(html_entity_decode(cookie('think_var') == 'en-us' ? $vo['en_contents'] : $vo['contents']))) > 100 ? htmlspecialchars(mb_substr($plain, 0, 100, 'UTF-8')).'...' : htmlspecialchars($plain) ?>
</div>
<div class="news_time">{$vo.create_time|date='Y-m-d H:i:s'}</div>
</div>
</a>
{/volist}
</div>
<div style="width: 100%; text-align: center;" id="case_page"></div>
</div>
</div>
</div>
{include file="public/newfooter" }
<script>
layui.use(function(){
var laypage = layui.laypage;
// 自定义每页条数的选择项
laypage.render({
elem: 'case_page',
count: '{$count}',
curr: '{$page}',
limit: '{$size}',
prev: '<em>←</em>',
next: '<em>→</em>',
jump: function(obj, first){
if(!first){
window.location.href = '{:Url("news/index")}?page='+obj.curr;
}
}
});
});
$('a[href*="articles"]').parents(".search-row").addClass("hidden");
$(document).ready(function () {
if (window.location.href.indexOf("query") > -1) {
$(".results").css("visibility", "inherit")
} else {
}
if ($(window).width() > 768) {
$("a:not(a.cookieAccept)").each(function () {
// $(this).attr('target', '_blank');
});
}
});
</script>
</body>
</html>

View File

@@ -0,0 +1,69 @@
{include file="public/head"}
<style>
.evus_tit {
width: 100%;
font-size: 24px;
text-align: center;
line-height: 40px;
}
.evus_infor {
color: #666;
font-size: 14px;
text-align: center;
padding: 20px 0;
border-bottom: 1px dashed #ddd;
margin-bottom: 20px;
}
.evus_con {
padding-bottom: 10px;
border-bottom: 1px dashed #ddd;
font-size: 14px;
}
.evus_con img {
margin: 0 auto;
width: 80%;
margin: 10px 0;
}
.evus_con p,
.evus_con span {
font-size: 14px;
}
.evus_other {
margin-top: 10px;
line-height: 30px;
}
@media (max-width: 720px) {
.content {
width: 100%;
padding: 30px 0px 0px;
background: none;
}
}
</style>
<body style="background: #F8F8F8;display: flex; flex-direction: column; min-height: 100vh;">
<div class="zh_content">
{include file="public/newnav" }
</div>
<div class="container" style="flex: 1;">
<div class="modular-row callout bg-grey">
<div class="container">
<h1 class="evus_tit">{:cookie('think_var') == 'en-us' ? $info['en_title'] : $info['title']}</h1>
<div class="evus_infor">
{:lang('index.news_author')}admin {:lang('index.news_time')}{$info.create_time}
</div>
<div class="evus_con">
<?= html_entity_decode(cookie('think_var') == 'en-us' ? $info['en_contents'] : $info['contents']) ?>
</div>
</div>
</div>
</div>
{include file="public/newfooter" }
</body>
</html>

View File

@@ -0,0 +1,47 @@
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>{:lang('pay.wechat_pay_title')}</title>
<script type="text/javascript" src="/static/js/jquery-1.8.3.min.js"></script>
<script type="text/javascript" src="/static/js/jquery-weui.min.js"></script>
<link rel="stylesheet" href="/static/css/weui.min.css">
<link rel="stylesheet" href="/static/css/jquery-weui.min.css">
<script type="text/javascript">
$(document).on("click", "#confirm", function () {
//进行查询订单
$.ajax({
type: "POST",
url: "{:url('pay/h5result',['order_sn'=>$order_sn])}",//+tab,
dataType: "json",
success: function (data) {
$.alert(data.msg);
setTimeout(function () {
location.href = data.url; // 跳转
}, 1000);
}
});
})
$(document).on("click", "#repay", function () {
//进行重新支付
window.location.href="{:url('pay/pay',['order_sn'=>$order_sn])}";
})
</script>
</head>
<body>
<div class="weui-mask weui-mask--visible"></div>
<div class="weui-dialog weui-dialog--visible">
<div class="weui-dialog__hd">
<strong class="weui-dialog__title">{:lang('pay.dialog_title')}</strong>
</div>
<div class="weui-dialog__bd">{:lang('pay.payment_confirmation')}</div>
<div class="weui-dialog__ft">
<a href="javascript:;" class="weui-dialog__btn default" id="repay">{:lang('pay.repay_button')}</a>
<a href="javascript:;" class="weui-dialog__btn primary" id="confirm">{:lang('pay.confirm_button')}</a>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,207 @@
{include file="public/head"}
<link rel="stylesheet" href="/static/css/pcpay.css?v=2">
<style>
.payment-pending {
max-width: 760px;
margin: 0 auto;
text-align: left;
}
.payment-pending-title {
font-size: 24px;
font-weight: 700;
margin-bottom: 16px;
color: #1f2937;
}
.payment-pending-text {
font-size: 16px;
line-height: 1.8;
color: #4b5563;
margin-bottom: 12px;
}
.payment-pending-status {
font-size: 15px;
color: #b45309;
margin-bottom: 24px;
}
.payment-spinner {
display: inline-block;
width: 24px;
height: 24px;
border: 3px solid #e5e7eb;
border-top-color: #2563eb;
border-radius: 50%;
animation: sqpay-spin 0.8s linear infinite;
vertical-align: middle;
margin-right: 10px;
}
@keyframes sqpay-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
</style>
</head>
<body style="display: flex; flex-direction: column; min-height: 100vh;">
<div class="zh_content">
{include file="public/newnav" }
</div>
<div
class="container"
id="payment-success-page"
data-payment-confirmed="{$payment_confirmed}"
data-poll-status-url="{$poll_status_url}"
>
<div class="modular-row callout bg-grey" style="margin-top: 50px; flex: 1;">
<div class="container">
<div class="row">
<div class="col-md-12 text-left">
<div id="payment-pending-block" class="payment-pending" style="display: none;">
<p class="payment-pending-title">
<span class="payment-spinner"></span>{:lang('sqpay.notice_title')}
</p>
<p class="payment-pending-text">{:lang('sqpay.notice_text')}</p>
<p class="payment-pending-text">
{:str_replace('%order_sn%', $order_sn ?? '', lang('pay.order_submitted'))}
</p>
<p class="payment-pending-status" id="payment-pending-status">{:lang('sqpay.notice_text')}</p>
<div class="register">
<a href="{$repay_url}">
<button class="dj-btn">{:lang('pay.repay_button')}</button>
</a>
</div>
</div>
<div id="payment-success-block">
<p style="font-size: 20px">{:lang('pay.thank_you_message')}</p>
<p style="font-size: 18px;color: #880000">
{:str_replace('%order_sn%', $order_sn ?? '', lang('pay.order_submitted'))}
</p>
<p style="font-size: 15px">
{:str_replace('%country%', $country ?? '', lang('pay.processing_time_info'))}
</p>
{if $arrive_date_info}
<p style="font-size: 15px">
{$arrive_date_info}
</p>
{/if}
<p style="font-size: 20px;font-weight: bolder;color: red">
{:lang('pay.contact')}
</p>
</div>
</div>
<div class="register" id="payment-success-action">
<a href="{:url('visa/index')}">
<button class="dj-btn">{:lang('pay.visa_application_button')}</button>
</a>
</div>
</div>
</div>
</div>
</div>
{include file="public/newfooter" }
<script>
(function () {
var pageEl = document.getElementById('payment-success-page');
if (!pageEl) {
return;
}
var isConfirmed = pageEl.getAttribute('data-payment-confirmed') === '1';
var pollUrl = pageEl.getAttribute('data-poll-status-url');
var pendingBlock = document.getElementById('payment-pending-block');
var successBlock = document.getElementById('payment-success-block');
var successAction = document.getElementById('payment-success-action');
var pendingStatus = document.getElementById('payment-pending-status');
var pendingText = "{:lang('sqpay.notice_text')}";
var successText = "{:lang('controller.payment_success')}";
var pollTimer = null;
var maxAttempts = 30;
var attempt = 0;
function showPending() {
if (pendingBlock) {
pendingBlock.style.display = 'block';
}
if (successBlock) {
successBlock.style.display = 'none';
}
if (successAction) {
successAction.style.display = 'none';
}
}
function showSuccess() {
if (pendingBlock) {
pendingBlock.style.display = 'none';
}
if (successBlock) {
successBlock.style.display = 'block';
}
if (successAction) {
successAction.style.display = 'block';
}
}
if (isConfirmed || !pollUrl) {
showSuccess();
return;
}
showPending();
async function pollStatus() {
attempt++;
try {
var response = await fetch(pollUrl, {
method: 'GET',
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
});
var data = await response.json();
if (data.status === 1) {
if (pendingStatus) {
pendingStatus.textContent = successText;
}
window.clearInterval(pollTimer);
showSuccess();
return;
}
if (data.status === -1 && data.url) {
window.clearInterval(pollTimer);
window.location.href = data.url;
return;
}
if (pendingStatus) {
pendingStatus.textContent = data.msg || pendingText;
}
} catch (e) {
if (pendingStatus) {
pendingStatus.textContent = pendingText;
}
}
if (attempt >= maxAttempts) {
window.clearInterval(pollTimer);
if (pendingStatus) {
pendingStatus.textContent = pendingText;
}
}
}
pollStatus();
pollTimer = window.setInterval(pollStatus, 2000);
})();
</script>
</body>
</html>

View File

@@ -0,0 +1,34 @@
{include file="public/head" /}
<style>
body {
background-color: #f5f5f5;
}
.footer {
position: fixed;
bottom: 0;
}
</style>
<body>
<div class="wrap">
<div class="zh_content">
{include file="public/newnav" /}
</div>
<div class="layui-container">
<div class="container">
<section class="m-form-application-category-container active" style="margin-top: 20px;">
<div class="m-form-application-title">
<h2>{:lang('foot.cookie_policy')}</h2>
</div>
<div class="modular-row callout bg-grey">
<div class="row">
<div class="col-md-12 text-left" style="font-size: 18px">
{:lang('foot.cookie_policy_info')}
</div>
</div>
</div>
</section>
</div>
</div>
</div>
{include file="public/newfooter" /}
</body>

View File

@@ -0,0 +1,38 @@
{include file="public/head" /}
<style>
body {
background: #F8F8F8;
}
.footer {
position: fixed;
bottom: 0;
}
</style>
<body>
<div class="wrap">
<div class="zh_content">
{include file="public/newnav" /}
</div>
<div class="layui-container">
<div class="u-bg-greylight">
<div class="container">
<section class="m-form-application-category-container active" style="margin-top: 20px;">
<div class="m-form-application-title">
<h2>{:lang('foot.disclaimer')}</h2>
</div>
<div class="modular-row callout bg-grey">
<div class="row">
<div class="col-md-12 text-left" style="font-size: 18px">
{:lang('foot.disclaimer_info')}
</div>
</div>
</div>
</section>
</div>
</div>
</div>
</div>
{include file="public/newfooter" /}
</body>

View File

@@ -0,0 +1,37 @@
{include file="public/head" /}
<style>
.text-left h4 {
margin-top: 20px;
}
body {
background-color: #f5f5f5;
}
.footer {
position: fixed;
bottom: 0;
}
</style>
<body>
<div class="wrap">
<div class="zh_content">
{include file="public/newnav" /}
</div>
<div class="layui-container">
<div class="container">
<section class="m-form-application-category-container active" style="margin-top: 20px;">
<div class="m-form-application-title">
<h2>{:lang('foot.refund_policy')}</h2>
</div>
<div class="modular-row callout bg-grey">
<div class="row">
<div class="col-md-12 text-left" style="font-size: 18px">
{:lang('foot.refund_policy_info')}
</div>
</div>
</div>
</section>
</div>
</div>
</div>
{include file="public/newfooter" /}
</body>

View File

@@ -0,0 +1,35 @@
{include file="public/head" /}
<style>
body {
background: #F8F8F8;
}
.section {
padding: 0;
}
</style>
<body>
<div class="wrap">
<div class="zh_content">
{include file="public/newnav" /}
</div>
<div class="layui-container">
<div class="u-bg-greylight">
<div class="container">
<section class="m-form-application-category-container active" style="margin-top: 20px;">
<div class="m-form-application-title">
<h2>{:lang('foot.privacy_policy')}</h2>
</div>
<div class="modular-row callout bg-grey">
<div class="row">
<div class="col-md-12 text-left" style="font-size: 18px">
{:lang('foot.privacy_policy_info')}
</div>
</div>
</div>
</section>
</div>
</div>
</div>
</div>
{include file="public/newfooter" /}
</body>

View File

@@ -0,0 +1,71 @@
{include file="public/head" /}
<style>
.text-left h4 {
margin-top: 20px;
}
body {
background-color: #f5f5f5;
}
.card {
background: #ffffff;
padding: 40px 32px;
box-shadow: 0 10px 30px rgba(15, 23, 42, 0.06);
}
h1 {
margin: 0 0 16px;
font-size: 32px;
line-height: 1.3;
color: #111827;
}
.intro {
margin: 0 0 28px;
color: #6b7280;
font-size: 16px;
}
section {
margin-top: 28px;
padding-top: 22px;
border-top: 1px solid #e5e7eb;
}
h2 {
margin: 0 0 12px;
font-size: 22px;
}
p {
margin: 0 0 12px;
}
.sub-list {
margin-top: 8px;
}
.contact a {
color: #0f766e;
text-decoration: none;
}
.contact a:hover {
text-decoration: underline;
}
</style>
<body>
<div class="wrap">
<div class="zh_content">
{include file="public/newnav" /}
</div>
<div class="layui-container">
<div class="container">
<section class="m-form-application-category-container active" style="margin-top: 20px;">
<div class="m-form-application-title">
<h2>{:lang('foot.refund_policy')}</h2>
</div>
<div class="modular-row callout bg-grey">
<div class="row">
<div class="col-md-12 text-left" style="font-size: 18px">
{:lang('foot.refund_policy_info')}
</div>
</div>
</div>
</section>
</div>
</div>
</div>
{include file="public/newfooter" /}
</body>

View File

@@ -0,0 +1,35 @@
{include file="public/head" /}
<style>
.text-left h4 {
margin-bottom: 20px;
}
body {
background: #F8F8F8;
}
</style>
<body>
<div class="wrap">
<div class="zh_content">
{include file="public/newnav" /}
</div>
<div class="layui-container">
<div class="u-bg-greylight">
<div class="container">
<section class="m-form-application-category-container active" style="margin-top: 20px;">
<div class="m-form-application-title">
<h2>{:lang('foot.user_agreement')}</h2>
</div>
<div class="modular-row callout bg-grey">
<div class="row">
<div class="col-md-12 text-left" style="font-size: 18px">
{:lang('foot.user_agreement_info')}
</div>
</div>
</div>
</section>
</div>
</div>
</div>
</div>
{include file="public/newfooter" /}
</body>

View File

@@ -0,0 +1,10 @@
<div class="footer">
<?= html_entity_decode($footer) ?>
<p><a href="#" class="termsCheckbox">{:lang('js.terms_conditions')}</a>&nbsp;|&nbsp;<a href="#" class="privacy_policy">{:lang('js.privacy_policy')}</a></p>
</div>
<style>
.footer a{
color: #ffffff;
}
</style>

View File

@@ -0,0 +1,98 @@
<!DOCTYPE html>
<html lang="" style="height:100%;">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{:lang('head.title')}</title>
<meta name="keywords" content="{:lang('head.keywords')}"/>
<meta name="description" content="{:lang('head.description')}"/>
<meta property="og:title" content="{:lang('head.og_title')}"/>
<meta property="og:description" content="{:lang('head.og_description')}"/>
<meta property="og:url" content="{$_SERVER['HTTP_HOST']}"/>
<meta property="og:type" content="website"/>
<link rel="icon" type="image/png" href="/static/image/icon.png">
<link rel="stylesheet" href="/static/layui/css/layui.css">
<script src="/static/layui/layui.js"></script>
<script src="/static/js/jquery.js"></script>
<script>
(function () {
function detectBasePath() {
var pathname = window.location.pathname || '';
var match = pathname.match(/^\/[^\/]+(?=\/home\/|\/static\/|\/json\/|\/$)/);
return match ? match[0] : '';
}
function normalizeBasePath(base) {
base = (base || '').trim();
// 模板变量未渲染时,退回到 URL 推断
if (!base || base === '/' || base.charAt(0) === '{') {
base = detectBasePath();
}
return base.replace(/\/+$/, '');
}
window.__BASE_PATH__ = normalizeBasePath('{$base_path}');
window.withBase = function (url) {
if (!url) {
return url;
}
if (/^(?:[a-z][a-z0-9+\-.]*:)?\/\//i.test(url) || /^[a-z][a-z0-9+\-.]*:/i.test(url)) {
return url;
}
if (url.charAt(0) !== '/') {
return url;
}
var base = window.__BASE_PATH__ || '';
if (!base) {
return url;
}
if (url === base || url.indexOf(base + '/') === 0) {
return url;
}
return base + url;
};
function attachAjaxPrefilter(jq) {
if (!jq || !jq.ajaxPrefilter || jq.__with_base_prefilter_attached__) {
return;
}
jq.__with_base_prefilter_attached__ = true;
jq.ajaxPrefilter(function (options) {
if (options && options.url) {
options.url = window.withBase(options.url);
}
});
}
attachAjaxPrefilter(window.jQuery);
attachAjaxPrefilter(window.$);
// 兼容 layui 可能持有独立 jQuery 实例
if (window.layui && window.layui.$) {
attachAjaxPrefilter(window.layui.$);
}
})();
</script>
<script src="/static/js/pub.js"></script>
<link rel="stylesheet" href="/static/css/tur_main.css?v={:time()}">
<link href="/static/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<!-- Bootstrap Icons -->
<link href="/static/bootstrap/css/bootstrap-icons.min.css" rel="stylesheet">
<link href="/static/css/newin.css?v={:time()}" rel="stylesheet">
<script type="text/javascript" async="" src="/static/js/footer.js"></script>
<script type="text/javascript" async="" src="/static/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="/static/js/passport-ocr-sdk.min.js?v=1"></script>
<script>
$(function(){
var footHight=$(".footer").height();
var docHeight=document.documentElement.clientHeight;
var bodyHeight=$("body").height();
var subHeight=docHeight-bodyHeight-footHight;
if(subHeight>30){
$(".footer").css({"position":"fixed","bottom":"0","z-index":"1000"});
$("body").css({"padding-bottom":"142px"});
} else {
$(".footer").css({"position": "static"});
$("body").css({"padding-bottom": "0"});
}
$(".footer").show();
})
</script>
</head>

View File

@@ -0,0 +1,46 @@
{include file="public/head"}
<link rel="stylesheet" href="/static/css/jump.css?v=1">
<body class="jump-page">
<div class="zh_content">
{include file="public/newnav" }
</div>
<main class="jump-shell">
<section class="jump-panel jump-panel--{$type|default='success'}">
<div class="jump-icon" aria-hidden="true"></div>
<div class="jump-content">
<h1 class="jump-message">{$msg}</h1>
<p class="jump-countdown">
<span id="countdown">{$wait|default=3}</span> {:lang('jump.auto_redirect')}...
</p>
</div>
<div class="jump-actions">
<a href="{$url ?: 'javascript:history.back();'}" class="jump-button">{:lang('jump.redirect_now')}</a>
</div>
</section>
</main>
{include file="public/newfooter" }
<script>
// 倒计时显示
let time = {$wait|default=3};
const countdownEl = document.getElementById('countdown');
const targetUrl = "{$url|default=''}";
const timer = setInterval(() => {
time--;
countdownEl.textContent = time;
if (time <= 0) {
clearInterval(timer);
if (targetUrl) {
location.href = targetUrl;
} else {
history.back();
}
}
}, 1000);
</script>
</body>
</html>

View File

@@ -0,0 +1,136 @@
<div class="process" style="background: none;margin-top: 0">
<div class="ps-tit" style="color: #151423; font-weight: 600; font-size: 32px; letter-spacing: .2px;">
{:lang('index.why_choose_us_title')}
<p style="font-size: 15px; padding: 10px;">
{:lang('index.why_choose_us_subtitle')}
</p>
</div>
</div>
<section class="middle-box2">
<!-- <div class="">-->
<!-- <div class="image-box">-->
<!-- <img class="" src="__TMPL__/public/static/image/why-choose-us.webp">-->
<!-- </div>-->
<!-- <h2 class="">为什么选择我们?</h2>-->
<!-- <p class="">了解为什么我们在旅行证件行业处于领先地位,使旅客能够轻松飞行。</p>-->
<!-- <div class="mt30">-->
<!-- <a href="{:url('visa/index')}" tabindex="-1">-->
<!-- <button-->
<!-- class="">-->
<!-- 立即申请-->
<!-- <svg class="wh16" fill="currentColor"-->
<!-- xmlns="http://www.w3.org/2000/svg"-->
<!-- viewBox="0 0 448 512">-->
<!-- <path-->
<!-- d="M190.5 66.9l22.2-22.2c9.4-9.4 24.6-9.4 33.9 0L441 239c9.4 9.4 9.4 24.6 0 33.9L246.6 467.3c-9.4 9.4-24.6 9.4-33.9 0l-22.2-22.2c-9.5-9.5-9.3-25 .4-34.3L311.4 296H24c-13.3 0-24-10.7-24-24v-32c0-13.3 10.7-24 24-24h287.4L190.9 101.2c-9.8-9.3-10-24.8-.4-34.3z">-->
<!-- </path>-->
<!-- </svg>-->
<!-- </button>-->
<!-- </a>-->
<!-- </div>-->
<!-- </div>-->
<div class="right-box">
<div
class="item">
<svg class=""
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
<mask id="a" width="20" height="20" x="0" y="0" maskUnits="userSpaceOnUse" style="mask-type:alpha">
<path fill="#D9D9D9" d="M0 0h20v20H0z"></path>
</mask>
<g>
<path fill="currentColor"
d="M10 2a8 8 0 1 1 0 16 8 8 0 0 1 0-16Zm-.75 3.75V10c0 .25.125.484.334.625l3 2a.748.748 0 0 0 1.041-.21.748.748 0 0 0-.21-1.04L10.75 9.6V5.75A.748.748 0 0 0 10 5a.748.748 0 0 0-.75.75Z">
</path>
</g>
</svg>
<div class="">
<h6 class="">{:lang('index.convenient')}</h6>
<p class="">{:lang('index.convenient_desc')}</p>
</div>
</div>
<div
class="item">
<svg class=""
xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
<path
d="M7 10a4 4 0 1 0 0-8 4 4 0 0 0 0 8Zm-1.428 1.5A5.57 5.57 0 0 0 0 17.072c0 .512.416.928.928.928h12.144c.056 0 .11-.006.166-.016-2.385-1.722-3.12-4.406-3.222-6.256A5.533 5.533 0 0 0 8.43 11.5h-2.86Zm9.65-2.447-3.75 1.5a.754.754 0 0 0-.472.697c0 1.978.81 5.275 4.213 6.694a.742.742 0 0 0 .578 0C19.19 16.525 20 13.228 20 11.25a.754.754 0 0 0-.472-.697l-3.75-1.5a.744.744 0 0 0-.556 0Zm3.26 2.697c-.123 1.584-.85 3.647-2.982 4.678v-5.872l2.981 1.194Z">
</path>
</svg>
<div class="">
<h6 class="">{:lang('index.secure')}</h6>
<p class="">{:lang('index.secure_desc')}</p>
</div>
</div>
<div
class="item">
<svg class=""
xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24">
<path
d="M1.2 4.8c0-1.324 1.076-2.4 2.4-2.4h6v4.8c0 .664.536 1.2 1.2 1.2h4.8v1.448a6.602 6.602 0 0 0-4.8 6.352 6.6 6.6 0 0 0 2.764 5.374c-.12.018-.24.026-.364.026H3.6a2.402 2.402 0 0 1-2.4-2.4V4.8Zm14.4 2.4h-4.8V2.4l4.8 4.8Zm-3.6 9a5.4 5.4 0 1 1 10.8 0 5.4 5.4 0 0 1-10.8 0Zm7.924-1.624a.602.602 0 0 0-.848 0L16.8 16.852l-1.076-1.076a.602.602 0 0 0-.848 0 .602.602 0 0 0 0 .848l1.5 1.5a.602.602 0 0 0 .848 0l2.7-2.7a.602.602 0 0 0 0-.848Z">
</path>
</svg>
<div class="">
<h6 class="">{:lang('index.approval')}</h6>
<p class="">{:lang('index.approval_desc')}</p>
</div>
</div>
<div
class="item">
<svg class="" width="20"
height="20" viewBox="0 0 20 20" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path id="Vector"
d="M10 18C12.1217 18 14.1566 17.1571 15.6569 15.6569C17.1571 14.1566 18 12.1217 18 10C18 7.87827 17.1571 5.84344 15.6569 4.34315C14.1566 2.84285 12.1217 2 10 2C7.87827 2 5.84344 2.84285 4.34315 4.34315C2.84285 5.84344 2 7.87827 2 10C2 12.1217 2.84285 14.1566 4.34315 15.6569C5.84344 17.1571 7.87827 18 10 18ZM7.30625 7.16563C7.55312 6.46875 8.21563 6 8.95625 6H10.7781C11.8687 6 12.75 6.88438 12.75 7.97188C12.75 8.67813 12.3719 9.33125 11.7594 9.68437L10.75 10.2625C10.7437 10.6687 10.4094 11 10 11C9.58437 11 9.25 10.6656 9.25 10.25V9.82812C9.25 9.55937 9.39375 9.3125 9.62813 9.17812L11.0125 8.38438C11.1594 8.3 11.25 8.14375 11.25 7.975C11.25 7.7125 11.0375 7.50313 10.7781 7.50313H8.95625C8.85 7.50313 8.75625 7.56875 8.72188 7.66875L8.70937 7.70625C8.57188 8.09688 8.14062 8.3 7.75313 8.1625C7.36563 8.025 7.15938 7.59375 7.29688 7.20625L7.30937 7.16875L7.30625 7.16563ZM9 13C9 12.7348 9.10536 12.4804 9.29289 12.2929C9.48043 12.1054 9.73478 12 10 12C10.2652 12 10.5196 12.1054 10.7071 12.2929C10.8946 12.4804 11 12.7348 11 13C11 13.2652 10.8946 13.5196 10.7071 13.7071C10.5196 13.8946 10.2652 14 10 14C9.73478 14 9.48043 13.8946 9.29289 13.7071C9.10536 13.5196 9 13.2652 9 13Z">
</path>
</svg>
<div class="">
<h6 class="">{:lang('index.support')}</h6>
<p class="">{:lang('index.support_desc')}</p>
</div>
</div>
<div
class="item">
<svg xmlns="http://www.w3.org/2000/svg" class="" viewBox="0 0 512 512" fill="currentColor">
<path d="M258.9 48C141.92 46.42 46.42 141.92 48 258.9c1.56 112.19 92.91 203.54 205.1 205.1 117 1.6 212.48-93.9 210.88-210.88C462.44 140.91 371.09 49.56 258.9 48zm-3.68 152.11c.21-1.2.44-2.4.71-3.59a66.46 66.46 0 0116.29-31.21c12.89-13.73 31.16-21.31 51.45-21.31a74.05 74.05 0 0125.06 4.26 66.69 66.69 0 0126.27 17.2 68.15 68.15 0 0118 42.14 78.46 78.46 0 010 11.4 86.19 86.19 0 01-8.2 31q-.76 1.59-1.59 3.15c-1.11 2.07-2.3 4.1-3.58 6.06a79.47 79.47 0 01-8.63 11c-13.12 14-29.92 21.73-47.31 21.73a59.61 59.61 0 01-19.17-3.18 63.47 63.47 0 01-6.1-2.43 70.76 70.76 0 01-22.07-16.12 83.76 83.76 0 01-22-51.32q-.27-3.88-.18-7.68a75.62 75.62 0 011.05-11.08zm-149.73 24.34a59.87 59.87 0 015.2-20.64 56.76 56.76 0 012.78-5.3 54.49 54.49 0 017.19-9.56 55.62 55.62 0 0114-10.82 56.84 56.84 0 018.11-3.64 63.85 63.85 0 0133.35-2.39 57 57 0 0130.78 17 57.86 57.86 0 0115.41 38.62c.05 2.11 0 4.23-.15 6.38a71.58 71.58 0 01-6 23.84 69.49 69.49 0 01-5.73 10.42 65.39 65.39 0 01-15.76 16.57c-1.5 1.07-3.06 2.07-4.67 3.07a54.21 54.21 0 01-10 4.65 49.31 49.31 0 01-16.2 2.76c-.93 0-1.86 0-2.78-.08a47.6 47.6 0 01-5.48-.62 51.19 51.19 0 01-5.35-1.23 53.54 53.54 0 01-7.72-2.89c-.84-.39-1.66-.8-2.48-1.23-18-9.49-31.57-29.16-34.23-52.12-.12-1.05-.22-2.1-.29-3.16a66.59 66.59 0 01.02-9.63zm53.92 178.6a177.27 177.27 0 01-61.94-70.65 4 4 0 011.62-5.26C117.67 316.69 141.4 311 163.82 311c17 0 30.7 2 42.69 5.88a8 8 0 012.59 13.77c-23.35 19-38.4 42.54-45.47 70.75a2.77 2.77 0 01-4.22 1.65zM256 432a175.12 175.12 0 01-65.7-12.72 4 4 0 01-2.4-4.46c.4-2.05.84-3.92 1.23-5.48 7.12-28.43 24.76-52 51-68.18 23.29-14.35 53-22.25 83.52-22.25 31.16 0 60 7.58 83.48 21.91a2.72 2.72 0 01.91 3.67A176.1 176.1 0 01256 432z"/>
<path d="M161 295.28a47.6 47.6 0 01-5.48-.62 47.6 47.6 0 005.48.62zM134.64 178.13a55.62 55.62 0 00-14 10.82 54.49 54.49 0 00-7.19 9.56 54.49 54.49 0 017.19-9.56 55.62 55.62 0 0114-10.82zM216.17 257.89a71.58 71.58 0 006-23.84c.15-2.15.2-4.27.15-6.38q.08 3.15-.15 6.38a71.58 71.58 0 01-6 23.84zM134.64 178.13a56.84 56.84 0 018.11-3.64 56.84 56.84 0 00-8.11 3.64zM150.21 293.43a53.54 53.54 0 01-7.72-2.89 53.54 53.54 0 007.72 2.89zM105.78 237.19c2.66 23 16.26 42.63 34.23 52.12-18.01-9.49-31.57-29.16-34.23-52.12zM254.34 219a83.76 83.76 0 0022 51.32 70.76 70.76 0 0022.07 16.12 70.76 70.76 0 01-22.07-16.12 83.76 83.76 0 01-22-51.32q-.27-3.88-.18-7.68-.09 3.75.18 7.68zM304.5 288.82a63.47 63.47 0 01-6.1-2.43 63.47 63.47 0 006.1 2.43zM255.93 196.54a66.46 66.46 0 0116.29-31.21 66.46 66.46 0 00-16.29 31.21zM375 165.46a68.15 68.15 0 0118 42.14 68.15 68.15 0 00-18-42.14 66.69 66.69 0 00-26.27-17.2 66.69 66.69 0 0126.27 17.2zM393 219a86.19 86.19 0 01-8.2 31 86.19 86.19 0 008.2-31zM254.16 211.27a75.62 75.62 0 011.06-11.14 75.62 75.62 0 00-1.06 11.14zM383.19 253.16zM206.88 189.05a57.86 57.86 0 0115.41 38.62 57.86 57.86 0 00-15.41-38.62 57 57 0 00-30.78-17 57 57 0 0130.78 17zM190 288a54.21 54.21 0 01-10 4.65 54.21 54.21 0 0010-4.65zM105.49 224.45a59.87 59.87 0 015.2-20.64 59.87 59.87 0 00-5.2 20.64zM194.68 284.88C193.17 286 191.61 287 190 288c1.61-1 3.17-2 4.68-3.12zM216.17 257.89a69.49 69.49 0 01-5.73 10.42 69.49 69.49 0 005.73-10.42zM110.69 203.81a56.76 56.76 0 012.78-5.3 56.76 56.76 0 00-2.78 5.3zM194.68 284.88a65.39 65.39 0 0015.76-16.57 65.39 65.39 0 01-15.76 16.57z"/>
</svg>
<div class="">
<h6 class="">{:lang('index.clients')}</h6>
<p class="">{:lang('index.clients_desc')}</p>
</div>
</div>
<div
class="item">
<svg xmlns="http://www.w3.org/2000/svg" class="" viewBox="0 0 512 512" fill="currentColor">
<path d="M479.07 111.36a16 16 0 00-13.15-14.74c-86.5-15.52-122.61-26.74-203.33-63.2a16 16 0 00-13.18 0C168.69 69.88 132.58 81.1 46.08 96.62a16 16 0 00-13.15 14.74c-3.85 61.11 4.36 118.05 24.43 169.24A349.47 349.47 0 00129 393.11c53.47 56.73 110.24 81.37 121.07 85.73a16 16 0 0012 0c10.83-4.36 67.6-29 121.07-85.73a349.47 349.47 0 0071.5-112.51c20.07-51.19 28.28-108.13 24.43-169.24zm-131 75.11l-110.8 128a16 16 0 01-11.41 5.53h-.66a16 16 0 01-11.2-4.57l-49.2-48.2a16 16 0 1122.4-22.86l37 36.29 99.7-115.13a16 16 0 0124.2 20.94z"/>
</svg>
<div class="">
<h6 class="">{:lang('index.service')}</h6>
<p class="">{:lang('index.service_desc')}</p>
</div>
</div>
<div
class="item">
<svg xmlns="http://www.w3.org/2000/svg" class="" viewBox="0 0 512 512" fill="currentColor">
<path d="M432.8 211.44c-15.52-8.82-34.91-2.28-43.31 13.68l-41.38 84.41a7 7 0 01-8.93 3.43 7 7 0 01-4.41-6.52V72c0-13.91-12.85-24-26.77-24s-26 10.09-26 24v156.64A11.24 11.24 0 01271.21 240 11 11 0 01260 229V24c0-13.91-10.94-24-24.86-24S210 10.09 210 24v204.64A11.24 11.24 0 01199.21 240 11 11 0 01188 229V56c0-13.91-12.08-24-26-24s-26 11.09-26 25v187.64A11.24 11.24 0 01125.21 256 11 11 0 01114 245V120c0-13.91-11.08-24-25-24s-25.12 10.22-25 24v216c0 117.41 72 176 160 176h16c88 0 115.71-39.6 136-88l68.71-169c6.62-18 3.6-34.75-11.91-43.56z"/>
</svg>
<div class="">
<h6 class="">{:lang('index.experience')}</h6>
<p class="">{:lang('index.experience_desc')}</p>
</div>
</div>
<div
class="item">
<svg xmlns="http://www.w3.org/2000/svg" class="" viewBox="0 0 512 512" fill="currentColor">
<path d="M256 448a32 32 0 01-18-5.57c-78.59-53.35-112.62-89.93-131.39-112.8-40-48.75-59.15-98.8-58.61-153C48.63 114.52 98.46 64 159.08 64c44.08 0 74.61 24.83 92.39 45.51a6 6 0 009.06 0C278.31 88.81 308.84 64 352.92 64c60.62 0 110.45 50.52 111.08 112.64.54 54.21-18.63 104.26-58.61 153-18.77 22.87-52.8 59.45-131.39 112.8a32 32 0 01-18 5.56z"/>
</svg>
<div class="">
<h6 class="">{:lang('index.approval_rate')}</h6>
<p class="">{:lang('index.approval_rate_desc')}</p>
</div>
</div>
</div>
</section>

View File

@@ -0,0 +1,284 @@
<style>
.zh_top {
display: flex;
padding-top: 5px;
padding-bottom: 10px;
}
.zh_head {
width: 40%;
justify-content: right;
}
.zh_hcon {
margin: unset;
width: unset;
}
.zh_nav {
width: 50%;
height: 70px;
position: relative;
}
@media (max-width: 1620px) and (min-width: 1201px){
.zh_nav {
width: 68%;
height: 70px;
position: relative;
}
}
@media (max-width: 1200px) {
.language-switcher {
top: 20%;
}
.zh_nav_btn {
display: block;
width: 30px;
height: 30px;
position: absolute;
right: 10px;
top: 20px;
cursor: pointer;
z-index: 1001;
}
.zh_nav {
width: 100%;
position: absolute;
left: 0;
background-color: var(--main-color);
display: none;
z-index: 1001;
}
/* 点击展开时显示 */
.zh_nav.active {
display: block;
}
.nav_con {
display: flex;
flex-direction: column;
width: 100%;
padding: 0;
margin: 0;
}
.nav_con li {
width: 100%;
border-bottom: 1px solid #eee;
list-style: none;
}
.nav_con li a {
display: block;
width: 100%;
text-align: center;
color: #fff;
text-decoration: none;
font-size: 15px;
background-color: var(--main-color);
}
.nav_con li a:hover {
background-color: #f8f8f8;
}
/* 语言切换按钮放在导航底部 */
.nav-language-switcher {
position: unset !important;
width: 100%;
padding: 10px 15px;
background-color: var(--main-color);
display: flex;
justify-content: flex-start;
}
.language-btn {
font-size: 13px;
padding: 5px 10px;
height: auto;
}
.language-dropdown {
position: relative;
width: auto;
border: none;
box-shadow: none;
}
.language-dropdown a {
padding: 8px 10px;
font-size: 13px;
}
}
/* 语言切换按钮容器 */
.language-switcher {
position: absolute;
display: inline-block;
right: 20%;
}
/* 语言切换按钮样式 */
.language-btn {
background-color: #f8f8f8;
border: 1px solid #ddd;
border-radius: 4px;
padding: 5px 10px;
font-size: 14px;
cursor: pointer;
display: flex;
align-items: center;
height: 40px;
}
.language-btn:hover {
background-color: #e8e8e8;
}
/* 下拉菜单样式 */
.language-dropdown {
display: none;
position: absolute;
/*right: 0;*/
top: 100%;
background-color: white;
border: 1px solid #ddd;
border-radius: 4px;
/*width: 100%;*/
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
z-index: 1000;
}
.language-dropdown a {
padding: 5px;
width: 100%;
display: block;
text-decoration: none;
color: #333;
font-size: 14px;
float: unset;
line-height: 40px;
}
.language-dropdown a:hover {
background-color: #f5f5f5;
display: block;
text-decoration: none;
color: #333;
font-size: 14px;
width: 100%;
float: unset;
line-height: 40px;
}
/* 显示下拉菜单 */
.show-dropdown {
display: block;
}
ol, ul, dl {
margin-bottom: 0;
}
.nav_con {
padding: 0;
}
/* ===== 手机端语言切换显示控制 ===== */
@media (max-width: 1200px) {
/* 手机端显示 languageBtn1顶部 */
#language-switcher1 {
display: inline-flex !important;
}
/* 隐藏导航栏中的 languageBtn */
#languageBtn {
display: none !important;
}
}
/* ===== PC端反向控制 ===== */
@media (min-width: 1201px) {
/* PC端隐藏顶部语言切换显示导航栏内的 */
#language-switcher1 {
display: none !important;
}
#languageBtn {
display: inline-flex !important;
}
}
</style>
<div class="zh_top">
<div class="zh_head">
<div class="zh_hcon">
<div class="zh_logo"><a href="/"><img src="/static/image/logo.png" alt=""></a></div>
</div>
</div>
<div class="language-switcher clearfix-language-switcher" id="language-switcher1" style="display:none;">
<button class="language-btn" id="languageBtn1">
<span>{if $think_lang == 'en-us'}English{else/}中文 {/if}</span>
<span style="margin-left: 5px;"></span>
</button>
<div class="language-dropdown" id="languageDropdown1">
<a class="language" href="#" data-id="zh-cn">中文</a>
<a class="language" href="#" data-id="en-us">English</a>
</div>
</div>
<img class="zh_nav_btn" src="/static/image/menu.png" alt="">
<div class="zh_nav">
<ul class="nav_con">
<li class=""><a href="/">{:lang('index.home')}</a></li>
<li class=""><a href="{:url('visa/index')}">{:lang('index.online_application')}</a></li>
<li class=""><a href="{:url('lookup/index')}">{:lang('index.entry_card_query')}</a></li>
<li class=""><a href="{:url('contact/index')}">{:lang('contact.menu_contact')}</a></li>
<li class=""><a href="{:url('news/index')}">{:lang('index.usage_help')}</a></li>
<div class="language-switcher nav-language-switcher">
<button class="language-btn" id="languageBtn">
<span>{if $think_lang == 'en-us'}English{else/}中文 {/if}</span>
<span style="margin-left: 5px;"></span>
</button>
<div class="language-dropdown" id="languageDropdown">
<a class="language" href="#" data-id="zh-cn">中文</a>
<a class="language" href="#" data-id="en-us">English</a>
</div>
</div>
</ul>
</div>
</div>
<script>
$('.zh_nav_btn').click(function () {
$('.zh_nav').toggleClass('active');
});
</script>
<div class="swiper" style="height: 100px;background: var(--main-color)">
</div>
<script>
const languageBtn = document.getElementById('languageBtn');
const languageDropdown = document.getElementById('languageDropdown');
// 切换下拉菜单显示/隐藏
languageBtn.addEventListener('click', function() {
languageDropdown.classList.toggle('show-dropdown');
});
// 点击页面其他地方关闭下拉菜单
document.addEventListener('click', function(e) {
if (!languageBtn.contains(e.target) && !languageDropdown.contains(e.target)) {
languageDropdown.classList.remove('show-dropdown');
}
});
const languageBtn1 = document.getElementById('languageBtn1');
const languageDropdown1 = document.getElementById('languageDropdown1');
// 切换下拉菜单显示/隐藏
languageBtn1.addEventListener('click', function() {
languageDropdown1.classList.toggle('show-dropdown');
});
// 点击页面其他地方关闭下拉菜单
document.addEventListener('click', function(e) {
if (!languageBtn1.contains(e.target) && !languageDropdown1.contains(e.target)) {
languageDropdown1.classList.remove('show-dropdown');
}
});
$('.language').click(function () {
const lang = $(this).attr('data-id');
$.ajax({
url: '/home/index/langset',
type: 'post',
data: {
lang: lang
},
success: function (res) {
if (res.code === 1) {
window.location.reload();
}
}
})
})
</script>

View File

@@ -0,0 +1,103 @@
<footer>
<div class="container">
<div class="text-center">
<?= html_entity_decode($footer) ?>
<div>{if $company_address}{:lang('foot.company_address')}{$company_address}{/if}</div>
</div>
<div class="footer-divider"></div>
<div class="d-flex flex-column flex-md-row justify-content-between align-items-center gap-2">
<div></div>
<!-- <div class="d-flex gap-3">-->
<!-- <a href="/home/policy/user"><span>{:lang('foot.user_agreement')}</span></a>-->
<!-- <a href="/home/policy/privacy"><span>{:lang('foot.privacy_policy')}</span></a>-->
<!-- <a href="/home/policy/disclaimer"><span>{:lang('foot.disclaimer')}</span></a>-->
<!-- <a href="/home/policy/refund"><span>{:lang('foot.refund_policy')}</span></a>-->
<!-- <a href="/home/policy/cookie"><span>{:lang('foot.cookie_policy')}</span></a>-->
<!-- </div>-->
</div>
</div>
</footer>
<!-- ===== 新增:悬浮客服/返回顶部(小模块很实用) ===== -->
<div class="float-help">
<a class="float-btn" href="/home/contact" title="{:lang('contact.menu_contact')}"><i class="bi bi-chat-dots"></i></a>
<a class="float-btn" href="#" title="{:lang('contact.back_to_top')}" onclick="window.scrollTo({top:0,behavior:'smooth'});return false;">
<i class="bi bi-arrow-up"></i>
</a>
</div>
<script>
// 监听复选框点击
$('.termsCheckbox').click(function (e) {
e.preventDefault();
if (window.innerWidth <= 768) {
area = ['90%', '90%']
} else {
area = ['50%', '60%']
}
// 弹出条款内容的弹窗
layer.open({
type: 1, // 弹窗类型:页面层
title: '{:lang(\'js.terms_conditions\')}', content: `
<div style="padding: 20px;">
<h3>{:lang(\'js.terms_conditions\')}</h3>
<p></p><p><span style="font-size: 12px;">{:str_replace(\'%country%\', $country ?? \'\', lang(\'js.terms_intro\'))}</span></p>
<p><span style="font-size: 12px;">{:str_replace(\'%country%\', $country ?? \'\', lang(\'js.our_services\'))}</span></p>
<p><span style="font-size: 12px;">{:str_replace(\'%country%\', $country ?? \'\', lang(\'js.application_process\'))}</span></p>
<p><span style="font-size: 12px;">{:lang(\'js.read_confirmation\')}</span></p>
<p><br></p><p></p>
</div>
`, area: area, // 弹窗大小
btn: ['关闭'], // 按钮
yes: function (index) {
layer.close(index); // 关闭弹窗
}
});
});
$('.privacy_policy').click(function (e) {
e.preventDefault();
if (window.innerWidth <= 768) {
area = ['90%', '90%']
} else {
area = ['50%', '60%']
}
// 弹出条款内容的弹窗
layer.open({
type: 1, // 弹窗类型:页面层
title: '{:lang(\'js.privacy_policy\')}', content: `
<div style="padding: 20px;">
<h3>{:lang(\'js.privacy_policy\')}</h3>
<p></p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.privacy_intro\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">1. {:lang(\'js.scope\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.scope_content\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">2. {:lang(\'js.information_use\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.information_use_content\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">3. {:lang(\'js.information_disclosure\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.information_disclosure_content\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">4. {:lang(\'js.information_storage\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.information_storage_content\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">5. {:lang(\'js.cookie_use\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.cookie_use_content\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">6. {:lang(\'js.information_security\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.information_security_content\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">7. {:lang(\'js.policy_changes\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.policy_changes_content\')}</p>
<p><br></p><p></p>
</div>
`, area: area, // 弹窗大小
btn: ['关闭'], // 按钮
yes: function (index) {
layer.close(index); // 关闭弹窗
}
});
});
</script>

View File

@@ -0,0 +1,73 @@
<!-- ===== Topbar / Navbar ===== -->
<header class="topbar">
<nav class="navbar navbar-expand-xl">
<div class="container py-2 topbar-inner">
<!-- 顶栏:永远一行 -->
<div class="topbar-head">
<a class="brand-wrap" href="#">
<span class="brand-badge brand-badge-logo"></span>
<span>
<div class="brand-title">{$country}TDAC</div>
<div class="brand-sub">Online Application Service</div>
</span>
</a>
<div class="topbar-right">
<!-- 手机端语言:始终显示,不进折叠菜单 -->
<div class="dropdown lang-mobile d-lg-none">
<button class="btn btn-sm lang-btn dropdown-toggle" data-bs-toggle="dropdown">{if $think_lang == 'en-us'}English{else/}中文 {/if}</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item language" data-id="zh-cn" href="#">中文</a></li>
<li><a class="dropdown-item language" data-id="en-us" href="#">English</a></li>
</ul>
</div>
<button class="navbar-toggler d-xl-none text-white border-0" type="button"
data-bs-toggle="collapse" data-bs-target="#nav">
<span class="navbar-toggler-icon" style="filter: invert(1)"></span>
</button>
</div>
</div>
<!-- 折叠菜单:永远在下一行展开 -->
<div class="collapse navbar-collapse" id="nav">
<ul class="navbar-nav ms-auto align-items-lg-center gap-lg-2 mt-2 mt-lg-0">
<li class="nav-item"><a class="nav-link" href="{$base_path}/">{:lang('index.home')}</a></li>
<li class="nav-item"><a class="nav-link" href="{:url('visa/index')}">{:lang('index.online_application')}</a></li>
<li class="nav-item"><a class="nav-link" href="{:url('lookup/index')}">{:lang('index.entry_card_query')}</a></li>
<li class="nav-item"><a class="nav-link" href="{:url('contact/index')}">{:lang('contact.menu_contact')}</a></li>
<li class="nav-item"><a class="nav-link" href="{:url('news/index')}">{:lang('index.usage_help')}</a></li>
<!-- PC 端语言:仍放在菜单里(手机端隐藏避免重复) -->
<li class="nav-item dropdown ms-lg-2 d-none d-lg-block">
<button class="btn btn-sm lang-btn dropdown-toggle" data-bs-toggle="dropdown">{if $think_lang == 'en-us'}English{else/}中文 {/if}</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item language" data-id="zh-cn" href="#">中文</a></li>
<li><a class="dropdown-item language" data-id="en-us" href="#">English</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
</header>
<script>
$('.language').click(function () {
const lang = $(this).attr('data-id');
$.ajax({
url: '/home/index/langset',
type: 'post',
data: {
lang: lang
},
success: function (res) {
if (res.code === 1) {
window.location.reload();
}
}
})
})
</script>

View File

@@ -0,0 +1,134 @@
{include file="public/head"}
<link rel="stylesheet" href="/static/css/page-sqpay.css?v=1">
<script type="text/javascript" src="https://web.squarecdn.com/v1/square.js"></script>
<script type="text/javascript">
window.applicationId = "{$applicationId}";
window.locationId = "{$locationId}";
window.currency = "{$currency}";
window.country = "{$pay_country}";
window.idempotencyKey = "{$uid}";
</script>
</head>
<body class="sqpay-page">
<div class="sqpay-layout">
{include file="public/newnav"}
<main class="sqpay-main">
<div class="sqpay-shell">
<section class="sqpay-card">
<div class="sqpay-heading">
<h1 class="sqpay-title">{:lang('sqpay.payment_title')}</h1>
<p class="sqpay-order">
{:str_replace('%order_sn%', $order_sn ?? '', lang('pay.order_submitted'))}
</p>
</div>
<div class="sqpay-amount">
<p class="sqpay-amount-label">{:lang('sqpay.total_amount_label')}</p>
<p class="sqpay-amount-value">{$total_fee} {$currency}</p>
</div>
<div class="sqpay-grid">
<section class="sqpay-method">
<header class="sqpay-method-head">
<h2 class="sqpay-method-title">{:lang('sqpay.card_pay_title')}</h2>
<div class="sqpay-brand-list">
<img alt="Visa" src="/static/image/pay/visa.png">
<img alt="Mastercard" src="/static/image/pay/logo_mastercard.png">
<img alt="JCB" src="/static/image/pay/logo_jcb.png">
<img alt="Discover" src="/static/image/pay/logo_discover.png">
<img alt="Diners Club" src="/static/image/pay/logo_diners.png">
<img alt="CB" src="/static/image/pay/logo_cb.png">
<img alt="Amex" src="/static/image/pay/logo_amex.png">
</div>
</header>
<div class="sqpay-method-body">
<form class="payment-form" id="fast-checkout" autocomplete="off">
<div id="card-container"></div>
<button id="card-button" class="sqpay-pay-button" type="button">{:lang('sqpay.pay_now')}</button>
</form>
</div>
</section>
<aside class="sqpay-notice">
<h3 class="sqpay-notice-head">{:lang('sqpay.notice_title')}</h3>
<p class="sqpay-notice-body">{:lang('sqpay.notice_text')}</p>
</aside>
</div>
</section>
</div>
</main>
{include file="public/newfooter"}
</div>
<script>
const order_sn = "{$order_sn}";
const total_price = "{$total_price}";
const paySuccessText = "{:lang('sqpay.pay_success')}";
const payFailedText = "{:lang('sqpay.pay_failed')}";
async function CardPay(fieldEl, buttonEl) {
// Create a card payment object and attach to page
const card = await window.payments.card();
await card.attach(fieldEl);
async function eventHandler(event) {
try {
const result = await card.tokenize();
if (result.status === 'OK') {
window.createPayment(result.token);
}
} catch (e) {
console.log( e.message)
layer.msg(payFailedText, {icon: 2, time: 2000})
layer.closeAll();
}
}
buttonEl.addEventListener('click', eventHandler);
layer.closeAll();
}
async function SquarePaymentFlow() {
var load = layer.load(2, {shade: 0.3})
CardPay(document.getElementById('card-container'), document.getElementById('card-button'));
}
window.payments = Square.payments(window.applicationId, window.locationId);
window.createPayment = async function (token) {
var load = layer.load(2, {shade: 0.3})
const dataJsonString = JSON.stringify({
token,
idempotencyKey: window.idempotencyKey,
order_sn: order_sn,
amount: total_price,
});
try {
const response = await fetch(`{:url('sqpay/paySquare')}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: dataJsonString
});
const data = await response.json();
if (data['code'] == 1) {
// 跳转成功页面
layer.msg(paySuccessText, {icon: 1, time: 2000}, function () {
window.location.href = data.url;
layer.closeAll();
})
} else {
layer.msg(payFailedText, {icon: 2, time: 2000})
layer.closeAll();
}
} catch (error) {
layer.msg(payFailedText, {icon: 2, time: 2000})
layer.closeAll();
}
}
SquarePaymentFlow();
</script>
</body>
</html>

View File

@@ -0,0 +1,242 @@
{include file="public/head" /}
<style>
body.user-page {
min-height: 100vh;
display: flex;
flex-direction: column;
background: #f5f7fb;
}
.user-main {
flex: 1;
padding: 32px 0 52px;
}
.account-layout {
display: grid;
grid-template-columns: 240px minmax(0, 1fr);
gap: 18px;
align-items: start;
}
.account-nav,
.account-panel {
background: #fff;
border: 1px solid rgba(15, 23, 42, .08);
border-radius: 8px;
box-shadow: 0 8px 24px rgba(15, 23, 42, .06);
overflow: hidden;
}
.account-nav__title,
.account-panel__title {
background: var(--main-color);
color: #fff;
font-size: 17px;
font-weight: 800;
line-height: 50px;
padding: 0 20px;
}
.account-nav__link {
display: flex;
align-items: center;
gap: 9px;
height: 50px;
padding: 0 18px;
color: #263241;
border-bottom: 1px solid #eef1f5;
text-decoration: none;
font-size: 15px;
}
.account-nav__link:hover,
.account-nav__link.is-active {
color: var(--main-color);
background: rgba(21, 56, 84, .06);
text-decoration: none;
}
.account-panel__body {
padding: 18px;
}
.order-list {
display: grid;
gap: 14px;
}
.order-card {
border: 1px solid #dfe5ee;
border-radius: 8px;
overflow: hidden;
background: #fff;
}
.order-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.order-item {
display: grid;
grid-template-columns: 96px minmax(0, 1fr);
gap: 8px;
padding: 12px 14px;
border-bottom: 1px solid #edf1f5;
color: #334155;
line-height: 1.55;
min-width: 0;
}
.order-item:nth-child(odd) {
border-right: 1px solid #edf1f5;
}
.order-item strong {
color: #0f172a;
font-weight: 700;
white-space: nowrap;
}
.order-item span {
word-break: break-word;
}
.order-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
padding: 12px 14px;
background: #f8fafc;
}
.account-btn {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 92px;
height: 36px;
padding: 0 16px;
border-radius: 4px;
background: var(--main-color);
color: #fff;
text-decoration: none;
font-weight: 700;
}
.account-btn:hover {
color: #fff;
background: #0f2b42;
text-decoration: none;
}
.empty-state {
padding: 52px 20px;
text-align: center;
color: #64748b;
}
.empty-state .account-btn {
margin-top: 16px;
}
@media (max-width: 768px) {
.user-main {
padding: 20px 12px 36px;
}
.account-layout {
grid-template-columns: 1fr;
gap: 12px;
}
.account-nav {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.account-nav__title {
display: none;
}
.account-nav__link {
justify-content: center;
padding: 0 8px;
font-size: 14px;
border-right: 1px solid #eef1f5;
}
.order-grid {
grid-template-columns: 1fr;
}
.order-item {
grid-template-columns: 88px minmax(0, 1fr);
}
.order-item:nth-child(odd) {
border-right: 0;
}
}
</style>
<body class="user-page">
{include file="public/newnav" /}
<main class="user-main">
<div class="layui-container">
<div class="account-layout">
<aside class="account-nav">
<div class="account-nav__title">个人中心</div>
<a href="{:url('user/index')}" class="account-nav__link is-active">
<i class="bi bi-receipt"></i><span>我的订单</span>
</a>
<a href="{:url('user/invoice')}" class="account-nav__link">
<i class="bi bi-file-earmark-text"></i><span>自助开票</span>
</a>
<a href="{:url('visa/index')}" class="account-nav__link">
<i class="bi bi-send"></i><span>签证申请</span>
</a>
</aside>
<section class="account-panel">
<div class="account-panel__title">我的订单</div>
<div class="account-panel__body">
<div class="order-list">
{volist name="order_list" id="vo"}
<article class="order-card">
<div class="order-grid">
<div class="order-item"><strong>订单号</strong><span>{$vo.order_sn}</span></div>
<div class="order-item"><strong>姓名</strong><span>{$vo.last_name} {$vo.first_name}</span></div>
<div class="order-item"><strong>出生日期</strong><span>{$vo.birth_date}</span></div>
<div class="order-item"><strong>护照号</strong><span>{$vo.passport_number}</span></div>
<div class="order-item"><strong>手机号</strong><span>{$vo.phone}</span></div>
<div class="order-item"><strong>下单时间</strong><span>{$vo.reg_time}</span></div>
<div class="order-item"><strong>状态</strong><span>{$vo.order_text}</span></div>
</div>
<div class="order-actions">
{eq name="$vo.pay_status" value="1"}
{eq name="$vo.invoice" value="0"}
<a class="account-btn" href="{:url('invoice/index',array('order_sn'=>$vo['order_sn']))}">开发票</a>
{/eq}
{/eq}
</div>
</article>
{/volist}
</div>
{empty name="order_list"}
<div class="empty-state">
<div>暂无订单记录</div>
<a class="account-btn" href="{:url('visa/index')}">去登记</a>
</div>
{/empty}
</div>
</section>
</div>
</div>
</main>
{include file="public/newfooter" /}
</body>
</html>

View File

@@ -0,0 +1,273 @@
{include file="public/head" /}
<style>
body.user-page {
min-height: 100vh;
display: flex;
flex-direction: column;
background: #f5f7fb;
}
.user-main {
flex: 1;
padding: 32px 0 52px;
}
.account-layout {
display: grid;
grid-template-columns: 240px minmax(0, 1fr);
gap: 18px;
align-items: start;
}
.account-nav,
.account-panel {
background: #fff;
border: 1px solid rgba(15, 23, 42, .08);
border-radius: 8px;
box-shadow: 0 8px 24px rgba(15, 23, 42, .06);
overflow: hidden;
}
.account-nav__title,
.account-panel__title {
background: var(--main-color);
color: #fff;
font-size: 17px;
font-weight: 800;
line-height: 50px;
padding: 0 20px;
}
.account-nav__link {
display: flex;
align-items: center;
gap: 9px;
height: 50px;
padding: 0 18px;
color: #263241;
border-bottom: 1px solid #eef1f5;
text-decoration: none;
font-size: 15px;
}
.account-nav__link:hover,
.account-nav__link.is-active {
color: var(--main-color);
background: rgba(21, 56, 84, .06);
text-decoration: none;
}
.account-panel__body {
padding: 22px;
}
.invoice-tip {
margin: 0 0 20px;
padding: 12px 14px;
border: 1px solid rgba(149, 30, 20, .16);
border-radius: 6px;
color: #951e14;
background: rgba(149, 30, 20, .05);
line-height: 1.7;
text-align: center;
}
.invoice-tip a {
color: var(--main-color);
font-weight: 800;
text-decoration: none;
}
.invoice-form .layui-form-label {
display: block;
float: none;
width: auto;
padding: 0 0 7px;
color: #1f2937;
text-align: left;
font-weight: 800;
}
.invoice-form .layui-form-label span {
color: #e53935;
margin-left: 3px;
}
.invoice-form .layui-input-block {
margin-left: 0;
min-height: auto;
}
.invoice-form .layui-input {
height: 50px;
border: 1px solid #d8dee8;
border-radius: 4px;
font-size: 15px;
}
.invoice-form .layui-input:focus {
border-color: var(--main-color) !important;
box-shadow: 0 0 0 3px rgba(21, 56, 84, .08);
}
.invoice-form .layui-btn {
height: 50px;
line-height: 50px;
border-radius: 6px;
background: var(--main-color);
font-size: 17px;
font-weight: 800;
}
@media (max-width: 768px) {
.user-main {
padding: 20px 12px 36px;
}
.account-layout {
grid-template-columns: 1fr;
gap: 12px;
}
.account-nav {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.account-nav__title {
display: none;
}
.account-nav__link {
justify-content: center;
padding: 0 8px;
font-size: 14px;
border-right: 1px solid #eef1f5;
}
.account-panel__body {
padding: 18px;
}
}
</style>
<body class="user-page">
{include file="public/newnav" /}
<main class="user-main">
<div class="layui-container">
<div class="account-layout">
<aside class="account-nav">
<div class="account-nav__title">个人中心</div>
<a href="{:url('user/index')}" class="account-nav__link">
<i class="bi bi-receipt"></i><span>我的订单</span>
</a>
<a href="{:url('user/invoice')}" class="account-nav__link is-active">
<i class="bi bi-file-earmark-text"></i><span>自助开票</span>
</a>
<a href="{:url('user/multiple')}" class="account-nav__link">
<i class="bi bi-files"></i><span>合并开票</span>
</a>
</aside>
<section class="account-panel">
<div class="account-panel__title">自助开票</div>
<div class="account-panel__body">
<p class="invoice-tip">
适用于单个订单开票。多个订单请使用
<a href="{:url('user/multiple')}">合并开票</a>
</p>
<form class="layui-form invoice-form" id="lookupform" name="form1" autocomplete="off">
<div class="layui-row layui-col-space24">
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label" for="passport_number">护照号码<span>*</span></label>
<div class="layui-input-block">
<input type="text" id="passport_number" name="passport_number"
maxlength="9" minlength="9" lay-verify="required"
placeholder="输入护照号码" class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label" for="order_sn">订单号<span>*</span></label>
<div class="layui-input-block">
<input type="text" id="order_sn" name="order_sn" lay-verify="required"
placeholder="输入订单号" class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label" for="last_name">姓氏<span>*</span></label>
<div class="layui-input-block">
<input type="text" id="last_name" name="last_name" maxlength="45"
lay-verify="required|englishName" placeholder="填写姓氏拼音"
class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label" for="first_name">名字<span>*</span></label>
<div class="layui-input-block">
<input type="text" id="first_name" name="first_name" maxlength="40"
lay-verify="required|englishName" placeholder="填写名字拼音"
class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12">
<button class="layui-btn layui-btn-fluid" lay-submit lay-filter="invoiceSubmit">提交</button>
</div>
</div>
</form>
</div>
</section>
</div>
</div>
</main>
{include file="public/newfooter" /}
<script>
layui.use(function () {
var $ = layui.$;
var form = layui.form;
var layer = layui.layer;
form.verify({
englishName: [/^[A-Za-z]+$/, '请填写英文拼音']
});
form.on('submit(invoiceSubmit)', function () {
var index1;
$.ajax({
type: 'POST',
url: "{:url('user/checkInvoice')}",
data: $('#lookupform').serialize(),
beforeSend: function () {
index1 = layer.load(1, {shade: [0.3, '#fff']});
},
success: function (data) {
layer.close(index1);
if (data.status == 1) {
layer.msg('查询成功');
window.location.href = data.url;
} else {
layer.msg(data.msg);
}
},
error: function () {
layer.close(index1);
layer.msg('请求失败,请稍后重试');
}
});
return false;
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,368 @@
{include file="public/head" /}
<style>
body.login-page {
min-height: 100vh;
display: flex;
flex-direction: column;
background: #f5f7fb;
}
.login-main {
flex: 1;
padding: 42px 0 56px;
}
.login-card {
max-width: 520px;
margin: 0 auto;
background: #fff;
border: 1px solid rgba(15, 23, 42, .08);
border-radius: 8px;
box-shadow: 0 8px 24px rgba(15, 23, 42, .08);
overflow: hidden;
}
.login-card__hd {
background: var(--main-color);
color: #fff;
padding: 18px 24px;
}
.login-card__hd h1 {
margin: 0;
font-size: 22px;
font-weight: 800;
line-height: 1.35;
letter-spacing: 0;
}
.login-card__hd p {
margin: 6px 0 0;
color: rgba(255, 255, 255, .76);
font-size: 13px;
line-height: 1.6;
}
.login-form {
padding: 28px 24px 26px;
}
.login-form .layui-form-item {
margin-bottom: 18px;
}
.login-form .layui-input-wrap {
width: 100%;
}
.login-form .layui-input {
height: 50px;
border: 1px solid #d8dee8;
border-radius: 4px;
font-size: 15px;
}
.login-form .layui-input:focus {
border-color: var(--main-color) !important;
box-shadow: 0 0 0 3px rgba(21, 56, 84, .08);
}
.login-code-row {
display: grid;
grid-template-columns: minmax(0, 1fr) 148px;
gap: 12px;
}
.login-get {
width: 100%;
height: 50px;
line-height: 50px;
padding: 0 12px;
border-radius: 4px;
border-color: var(--main-color);
color: var(--main-color);
background: #fff;
font-weight: 700;
}
.login-get:hover,
.login-get:focus {
border-color: var(--main-color);
color: var(--main-color);
background: rgba(21, 56, 84, .06);
}
.login-get[disabled] {
border-color: #d8dee8 !important;
color: #9aa3af !important;
background: #f6f7f9 !important;
cursor: not-allowed;
}
.login-submit {
height: 50px;
line-height: 50px;
border-radius: 6px;
background: var(--main-color);
border-color: var(--main-color);
font-size: 17px;
font-weight: 800;
}
.login-submit:hover,
.login-submit:focus {
background: #0f2b42;
border-color: #0f2b42;
}
.login-agreement {
margin-top: 2px;
color: #475569;
font-size: 13px;
line-height: 1.7;
}
.login-agreement a {
color: var(--main-color);
font-weight: 700;
text-decoration: none;
}
.login-agreement a:hover {
text-decoration: underline;
}
.layui-form-checked[lay-skin=primary] > i {
border-color: var(--main-color) !important;
background-color: var(--main-color);
color: #fff;
}
.login-terms {
padding: 20px;
max-height: 70vh;
overflow-y: auto;
color: #333;
line-height: 1.8;
}
.login-terms h3 {
margin: 10px 0 8px;
color: var(--main-color);
font-size: 18px;
font-weight: 700;
}
.login-terms p {
margin: 0 0 10px;
font-size: 13px;
}
@media (max-width: 768px) {
.login-main {
padding: 24px 12px 36px;
}
.login-card__hd,
.login-form {
padding-left: 18px;
padding-right: 18px;
}
.login-code-row {
grid-template-columns: 1fr;
gap: 10px;
}
}
</style>
<body class="login-page">
{include file="public/newnav" /}
<main class="login-main">
<div class="layui-container">
<section class="login-card">
<div class="login-card__hd">
<h1>用户登录</h1>
<p>使用手机号验证码登录后,可查看订单进度和已提交的申请信息。</p>
</div>
<form class="layui-form login-form" autocomplete="off">
<div class="layui-form-item">
<div class="login-code-row">
<div class="layui-input-wrap">
<div class="layui-input-prefix">
<i class="layui-icon layui-icon-cellphone"></i>
</div>
<input type="text" name="phone" value="" lay-verify="required|phone"
placeholder="{:lang('visa.phone_placeholder')}"
lay-reqtext="{:lang('visa.phone_placeholder')}" autocomplete="off"
class="layui-input" id="reg-cellphone">
</div>
<input class="login-get layui-btn layui-btn-primary" type="button"
id="getcode" value="获取验证码" lay-on="reg-get-vercode">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-wrap">
<div class="layui-input-prefix">
<i class="layui-icon layui-icon-vercode"></i>
</div>
<input type="text" name="vercode" value="" lay-verify="required"
placeholder="{:lang('contact.yzm')}" lay-reqtext="请填写验证码"
autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item login-agreement">
<input type="checkbox" lay-filter="termsCheckbox" name="agreement" lay-skin="primary"
title="{:lang('visa.terms_acceptance')}" checked
lay-verify="required|checkboxRequired">
</div>
<div class="layui-form-item" style="margin-bottom: 0;">
<button class="layui-btn layui-btn-fluid login-submit" lay-submit lay-filter="demo-reg">登录</button>
</div>
</form>
</section>
</div>
</main>
{include file="public/newfooter" /}
<script>
layui.use(function () {
var $ = layui.$;
var form = layui.form;
var layer = layui.layer;
var util = layui.util;
var wait = 60;
var timer = null;
form.verify({
checkboxRequired: function () {
if (!$('input[name="agreement"]').prop('checked')) {
return '请先阅读并接受《条款与条件》及《隐私政策》';
}
}
});
function getLayerArea() {
return window.innerWidth <= 768 ? ['90%', '90%'] : ['50%', '60%'];
}
function openTermsDialog() {
layer.open({
type: 1,
title: '{:lang(\'js.terms_privacy_title\')}',
area: getLayerArea(),
btn: ['关闭'],
content: `
<div class="login-terms">
<h3>{:lang('js.terms_conditions')}</h3>
<p>{:str_replace('%country%', $country ?? '', lang('js.terms_intro'))}</p>
<p>{:str_replace('%country%', $country ?? '', lang('js.our_services'))}</p>
<p>{:str_replace('%country%', $country ?? '', lang('js.application_process'))}</p>
<p>{:lang('js.read_confirmation')}</p>
<h3>{:lang('js.privacy_policy')}</h3>
<p>{:lang('js.privacy_intro')}</p>
<h3>1. {:lang('js.scope')}</h3>
<p>{:lang('js.scope_content')}</p>
<h3>2. {:lang('js.information_use')}</h3>
<p>{:lang('js.information_use_content')}</p>
<h3>3. {:lang('js.information_disclosure')}</h3>
<p>{:lang('js.information_disclosure_content')}</p>
<h3>4. {:lang('js.information_storage')}</h3>
<p>{:lang('js.information_storage_content')}</p>
<h3>5. {:lang('js.cookie_use')}</h3>
<p>{:lang('js.cookie_use_content')}</p>
<h3>6. {:lang('js.information_security')}</h3>
<p>{:lang('js.information_security_content')}</p>
<h3>7. {:lang('js.policy_changes')}</h3>
<p>{:lang('js.policy_changes_content')}</p>
</div>
`
});
}
form.on('checkbox(termsCheckbox)', function (data) {
if (data.elem.checked) {
openTermsDialog();
}
});
form.on('submit(demo-reg)', function (data) {
if (!data.field.agreement) {
layer.msg('请先阅读并接受《条款与条件》及《隐私政策》', {icon: 2});
return false;
}
$.ajax({
url: '/home/login/login',
type: 'post',
data: data.field,
success: function (res) {
if (res.code == 1) {
layer.msg('登录成功', {icon: 1}, function () {
window.location.href = res.url;
});
} else {
layer.msg(res.msg, {icon: 2});
}
},
error: function () {
layer.msg('请求失败,请稍后重试', {icon: 2});
}
});
return false;
});
function settime() {
var $code = $('#getcode');
if (wait <= 0) {
$code.removeAttr('disabled').val('获取验证码');
wait = 60;
timer = null;
return;
}
$code.prop('disabled', true).val('重新发送 ' + wait + 's');
wait--;
timer = setTimeout(settime, 1000);
}
util.on('lay-on', {
'reg-get-vercode': function () {
var isvalid = form.validate('#reg-cellphone');
if (isvalid) {
$.ajax({
url: '/home/login/login_code',
type: 'POST',
data: {mobile: $('#reg-cellphone').val()},
dataType: 'json',
success: function (data) {
if (data.code == 1) {
if (timer) {
clearTimeout(timer);
}
wait = 60;
settime();
} else {
layer.msg(data.msg, {icon: 2});
}
},
error: function () {
layer.msg('验证码发送失败,请稍后重试', {icon: 2});
}
});
}
}
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,387 @@
{include file="public/head" /}
<style>
body.user-page {
min-height: 100vh;
display: flex;
flex-direction: column;
background: #f5f7fb;
}
.user-main {
flex: 1;
padding: 32px 0 52px;
}
.account-layout {
display: grid;
grid-template-columns: 240px minmax(0, 1fr);
gap: 18px;
align-items: start;
}
.account-nav,
.account-panel {
background: #fff;
border: 1px solid rgba(15, 23, 42, .08);
border-radius: 8px;
box-shadow: 0 8px 24px rgba(15, 23, 42, .06);
overflow: hidden;
}
.account-nav__title,
.account-panel__title {
background: var(--main-color);
color: #fff;
font-size: 17px;
font-weight: 800;
line-height: 50px;
padding: 0 20px;
}
.account-nav__link {
display: flex;
align-items: center;
gap: 9px;
height: 50px;
padding: 0 18px;
color: #263241;
border-bottom: 1px solid #eef1f5;
text-decoration: none;
font-size: 15px;
}
.account-nav__link:hover,
.account-nav__link.is-active {
color: var(--main-color);
background: rgba(21, 56, 84, .06);
text-decoration: none;
}
.account-panel__body {
padding: 22px;
}
.invoice-tip {
margin: 0 0 20px;
padding: 12px 14px;
border: 1px solid rgba(149, 30, 20, .16);
border-radius: 6px;
color: #951e14;
background: rgba(149, 30, 20, .05);
line-height: 1.7;
text-align: center;
}
.invoice-tip a {
color: var(--main-color);
font-weight: 800;
text-decoration: none;
}
.invoice-row {
position: relative;
margin-bottom: 16px;
padding: 18px;
border: 1px solid #dfe5ee;
border-radius: 8px;
background: #fff;
}
.invoice-row__actions {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 4px;
}
.invoice-action {
display: inline-flex;
align-items: center;
gap: 5px;
color: var(--main-color);
cursor: pointer;
font-weight: 800;
user-select: none;
}
.invoice-action.is-danger {
color: #951e14;
}
.invoice-form .layui-form-label {
display: block;
float: none;
width: auto;
padding: 0 0 7px;
color: #1f2937;
text-align: left;
font-weight: 800;
}
.invoice-form .layui-form-label span {
color: #e53935;
margin-left: 3px;
}
.invoice-form .layui-input-block {
margin-left: 0;
min-height: auto;
}
.invoice-form .layui-input {
height: 50px;
border: 1px solid #d8dee8;
border-radius: 4px;
font-size: 15px;
}
.invoice-form .layui-input:focus {
border-color: var(--main-color) !important;
box-shadow: 0 0 0 3px rgba(21, 56, 84, .08);
}
.invoice-submit {
height: 50px;
line-height: 50px;
border-radius: 6px;
background: var(--main-color);
font-size: 17px;
font-weight: 800;
}
@media (max-width: 768px) {
.user-main {
padding: 20px 12px 36px;
}
.account-layout {
grid-template-columns: 1fr;
gap: 12px;
}
.account-nav {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.account-nav__title {
display: none;
}
.account-nav__link {
justify-content: center;
padding: 0 8px;
font-size: 14px;
border-right: 1px solid #eef1f5;
}
.account-panel__body {
padding: 18px;
}
}
</style>
<body class="user-page">
{include file="public/newnav" /}
<main class="user-main">
<div class="layui-container">
<div class="account-layout">
<aside class="account-nav">
<div class="account-nav__title">个人中心</div>
<a href="{:url('user/index')}" class="account-nav__link">
<i class="bi bi-receipt"></i><span>我的订单</span>
</a>
<a href="{:url('user/invoice')}" class="account-nav__link">
<i class="bi bi-file-earmark-text"></i><span>自助开票</span>
</a>
<a href="{:url('user/multiple')}" class="account-nav__link is-active">
<i class="bi bi-files"></i><span>合并开票</span>
</a>
</aside>
<section class="account-panel">
<div class="account-panel__title">合并开票</div>
<div class="account-panel__body">
<p class="invoice-tip">
适用于多个订单合并开票。单个订单请使用
<a href="{:url('user/invoice')}">自助开票</a>
</p>
<form class="layui-form invoice-form" id="lookupform" name="form1" autocomplete="off">
<div id="invoiceRows">
<div class="invoice-row">
<div class="layui-row layui-col-space24">
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">护照号码<span>*</span></label>
<div class="layui-input-block">
<input type="text" name="passport_number" maxlength="9" minlength="9"
lay-verify="required" placeholder="输入护照号码" class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">订单号<span>*</span></label>
<div class="layui-input-block">
<input type="text" name="order_sn" lay-verify="required"
placeholder="输入订单号" class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">姓氏<span>*</span></label>
<div class="layui-input-block">
<input type="text" name="last_name" maxlength="45"
lay-verify="required|englishName" placeholder="填写姓氏拼音"
class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">名字<span>*</span></label>
<div class="layui-input-block">
<input type="text" name="first_name" maxlength="40"
lay-verify="required|englishName" placeholder="填写名字拼音"
class="layui-input">
</div>
</div>
</div>
</div>
<div class="invoice-row__actions">
<span class="invoice-action js-add-row"><i class="layui-icon layui-icon-add-circle"></i>添加</span>
<span class="invoice-action is-danger js-remove-row"><i class="layui-icon layui-icon-reduce-circle"></i>删除</span>
</div>
</div>
</div>
<button class="layui-btn layui-btn-fluid invoice-submit" lay-submit lay-filter="multipleSubmit">提交</button>
</form>
</div>
</section>
</div>
</div>
</main>
{include file="public/newfooter" /}
<script>
layui.use(function () {
var $ = layui.$;
var form = layui.form;
var layer = layui.layer;
form.verify({
englishName: [/^[A-Za-z]+$/, '请填写英文拼音']
});
function refreshRemoveButtons() {
var $rows = $('#invoiceRows .invoice-row');
$rows.find('.js-remove-row').toggle($rows.length > 1);
}
function getRowData($row) {
return {
passport_number: $.trim($row.find("input[name='passport_number']").val()),
order_sn: $.trim($row.find("input[name='order_sn']").val()),
last_name: $.trim($row.find("input[name='last_name']").val()),
first_name: $.trim($row.find("input[name='first_name']").val())
};
}
function validateRow($row) {
var data = getRowData($row);
if (!data.passport_number) {
layer.msg('请输入护照号');
return false;
}
if (!data.order_sn) {
layer.msg('请输入订单号');
return false;
}
if (!data.last_name) {
layer.msg('请输入姓氏');
return false;
}
if (!data.first_name) {
layer.msg('请输入名字');
return false;
}
if (!/^[A-Za-z]+$/.test(data.last_name) || !/^[A-Za-z]+$/.test(data.first_name)) {
layer.msg('请填写英文拼音');
return false;
}
return true;
}
$(document).on('click', '.js-add-row', function () {
var $row = $(this).closest('.invoice-row');
if (!validateRow($row)) {
return false;
}
var $newRow = $row.clone(false, false);
$newRow.find('input').val('');
$('#invoiceRows').append($newRow);
refreshRemoveButtons();
});
$(document).on('click', '.js-remove-row', function () {
$(this).closest('.invoice-row').remove();
refreshRemoveButtons();
});
form.on('submit(multipleSubmit)', function () {
var datas = [];
var isValid = true;
var index1;
$('#invoiceRows .invoice-row').each(function () {
var $row = $(this);
if (!validateRow($row)) {
isValid = false;
return false;
}
datas.push(getRowData($row));
});
if (!isValid) {
return false;
}
$.ajax({
type: 'POST',
url: "{:url('user/checkMultiple')}",
data: {datas: JSON.stringify(datas)},
beforeSend: function () {
index1 = layer.load(1, {shade: [0.3, '#fff']});
},
success: function (data) {
layer.close(index1);
if (data.status == 1) {
layer.msg('查询成功');
window.location.href = data.url;
} else {
layer.msg(data.msg);
}
},
error: function () {
layer.close(index1);
layer.msg('请求失败,请稍后重试');
}
});
return false;
});
refreshRemoveButtons();
});
</script>
</body>
</html>

View File

@@ -0,0 +1,738 @@
{include file="public/head"}
<link rel="stylesheet" href="/static/css/visa.css?v={:time()}">
<link rel="stylesheet" href="https://files.jzvisa.com/visa/calendar/calendar.min.css"/>
<script src="https://files.jzvisa.com/visa/calendar/calendar.min.js"></script>
<script type="text/javascript" async="" src="/static/js/xm-select.js"></script>
<style>
.confirm-table td {
padding: 8px 12px !important;
vertical-align: middle;
word-break: break-word;
font-size: 16px;
}
.confirm-table tr:nth-child(even) {
background-color: #fafafa;
}
.confirm-table td:first-child {
font-weight: 600;
color: #333;
background-color: #f8f8f8;
width: 40%;
}
.confirm-note {
color: #e53e3e;
font-size: 14px;
margin-top: 12px;
text-align: center;
}
.layui-form-checkbox[lay-skin=primary]>div {
white-space: normal;
word-break: break-word;
word-wrap: break-word;
}
</style>
<body>
<div class="zh_content">
{include file="public/newnav"/}
</div>
<div class="layui-container contact-page">
<ol class="m-steps__classic m-steps__classic--progress">
<li class="current">
<div class="m-steps-textcontainer">1. {:lang('visa.step1_title')}</div>
<span></span>
</li>
<li>
<div class="m-steps-textcontainer">2. {:lang('visa.step2_title')}</div>
<span></span>
</li>
<li>
<div class="m-steps-textcontainer">3. {:lang('visa.step3_title')}</div>
<span></span>
</li>
</ol>
<div class="u-bg-greylight" style="background: #ffffff">
<!-- 第一步 -->
<form class="layui-form" lay-filter="Form1" id="Form1" autocomplete="off">
<div class="m-form-application-title">
<h2>{:lang('visa.personal_details_title')}</h2>
</div>
<div class="layui-row">
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('visa.passport_number')}<span>*</span></label>
<div class="layui-input-block">
<input type="text" name="passport_number" lay-verify="required|chinaPassport"
placeholder="{:lang('visa.passport_number_placeholder')}"
class="layui-input">
<input type="hidden" name="passport_img" value="">
<!-- 关键:给 file 一个 id并隐藏 -->
<input id="passport_file" class="passport-file" type="file" accept="image/*"
onchange="upload(this,'passport')">
<!-- 关键label 的 for 指向 file 的 id -->
<label class="passport-upload" for="passport_file" title="拍照/上传有效护照照片">
<i class="layui-icon layui-icon-camera"></i>
</label>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('visa.nationality')}<span>*</span></label>
<div class="layui-input-block">
<select lay-search="" name="nation" lay-verify="required">
<option value="">{:lang('visa.nationality_placeholder')}</option>
{volist name="nation" id="vo"}
<option value="{$vo['iso3']}" {$vo['iso3']=='CHN' ?'selected' : ''}>{:cookie('think_var')=='zh-cn' ? $vo['name_zh']: $vo['name_en'] }</option>
{/volist}
</select>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('visa.last_name_en')}<span>*</span></label>
<div class="layui-input-block">
<input type="text" name="last_name" lay-verify="required|englishOnly"
placeholder="{:lang('visa.last_name_placeholder')}"
class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('visa.first_name_en')}<span>*</span></label>
<div class="layui-input-block">
<input type="text" name="first_name" lay-verify="required|englishOnly"
placeholder="{:lang('visa.first_name_placeholder')}"
class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label" for="birth_date">{:lang('visa.birth_date')}<span>*</span></label>
<div class="layui-input-block">
<div class="layui-input-wrap">
<div class="layui-input-prefix">
<i class="layui-icon layui-icon-date"></i>
</div>
<input type="text" name="birth_date" id="birth_date" lay-verify="required"
placeholder="{:lang('visa.birth_date_placeholder')}"
class="layui-input layui-qian " readonly>
<div class="layui-input-suffix">
<i class="layui-icon layui-icon-down"></i>
</div>
</div>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label"
for="passport_expiration_date">{:lang('visa.passport_expiry')}<span>*</span></label>
<div class="layui-input-block">
<div class="layui-input-wrap">
<div class="layui-input-prefix">
<i class="layui-icon layui-icon-date"></i>
</div>
<input type="text" name="passport_expiration_date" id="passport_expiration_date"
lay-verify="required"
placeholder="{:lang('visa.passport_expiry_placeholder')}"
class="layui-input layui-hou" readonly>
<div class="layui-input-suffix">
<i class="layui-icon layui-icon-down"></i>
</div>
</div>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label" for="email">{:lang('visa.email_address')}<span>*</span></label>
<div class="layui-input-block">
<input type="text" name="email"
placeholder="{:lang('visa.email_placeholder')}"
class="layui-input" lay-verify="required|email">
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label" for="phone">{:lang('visa.phone_number')}<span>*</span></label>
<div class="layui-input-block">
<div class="layui-input-group">
<!-- 前缀 select -->
<div class="layui-input-group-prepend">
<select name="phone_prefix" lay-search lay-verify="required">
{volist name="phone_prefix" id="vo"}
<option value="{$vo['0']}">{$vo['1']}</option>
{/volist}
</select>
</div>
<input type="text" name="phone"
placeholder="{:lang('visa.phone_placeholder')}"
class="layui-input" lay-verify="required|number">
</div>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label" for="gender">{:lang('visa.gender')}<span>*</span></label>
<div class="layui-input-block">
<input type="radio" name="gender" value="1" title="{:lang('visa.gender_male')}" lay-verify="otherReq"/>
<input type="radio" name="gender" value="2" title="{:lang('visa.gender_female')}" lay-verify="otherReq"/>
</div>
</div>
</div>
</div>
<div class="layui-row text-center">
<div class="layui-col-xs12">
<div class="layui-form-item">
<button class="layui-btn btn-next" lay-submit>{:lang('visa.next_step')}</button>
</div>
</div>
</div>
</form>
<!-- 第二步 -->
<form class="layui-form" lay-filter="Form2" id="Form2" autocomplete="off">
<div class="m-form-application-title">
<h2>{:lang('visa.travel_information_title')}</h2>
</div>
<div class="layui-row">
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('visa.arrive_date')}<span>*</span></label>
<div class="layui-input-block">
<div class="layui-input-wrap">
<div class="layui-input-prefix">
<i class="layui-icon layui-icon-date"></i>
</div>
<input type="text" name="arrive_date" lay-verify="required"
placeholder="{:lang('visa.arrive_date_placeholder')}"
class="layui-input layui-arrive" readonly>
<div class="layui-input-suffix">
<i class="layui-icon layui-icon-down"></i>
</div>
</div>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('visa.leave_date')}<span>*</span></label>
<div class="layui-input-block">
<div class="layui-input-wrap">
<div class="layui-input-prefix">
<i class="layui-icon layui-icon-date"></i>
</div>
<input type="text" name="leave_date" lay-verify="required"
placeholder="{:lang('visa.leave_date_placeholder')}"
class="layui-input layui-leave" readonly>
<div class="layui-input-suffix">
<i class="layui-icon layui-icon-down"></i>
</div>
</div>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('visa.travel_way')}<span>*</span></label>
<div class="layui-input-block">
<select name="travel_way" lay-verify="required">
<option value="">{:lang('visa.travel_way_placeholder')}</option>
<option value="1">{:lang('visa.by_air')}</option>
<option value="2">{:lang('visa.by_sea')}</option>
<option value="3">{:lang('visa.by_land')}</option>
</select>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('visa.travel_country')}<span>*</span></label>
<div class="layui-input-block">
<select name="travel_country" lay-search lay-verify="required">
<option value="">{:lang('visa.travel_country_placeholder')}</option>
{volist name="travel_country" id="vo"}
<option value="{$vo['value']}">{$vo['text']}</option>
{/volist}
</select>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('visa.travel_number')}<span>*</span></label>
<div class="layui-input-block">
<input type="text" name="travel_number" lay-verify="required"
placeholder="{:lang('visa.travel_number_placeholder')}"
class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('visa.hotel_type')}<span>*</span></label>
<div class="layui-input-block">
<select name="hotel_type" lay-verify="required">
<option value="">{:lang('visa.hotel_type_placeholder')}</option>
<option value="01">{:lang('visa.guesthouse')}</option>
<option value="02">{:lang('visa.friend')}</option>
<option value="99">{:lang('visa.other')}</option>
</select>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('visa.hotel_state')}<span>*</span></label>
<div class="layui-input-block">
<select name="hotel_state" lay-search lay-filter="hotel_state_select"
lay-verify="required">
<option value="">{:lang('visa.hotel_state_placeholder')}</option>
<option value="01">JOHOR 柔佛</option>
<option value="02">KEDAH 吉打</option>
<option value="03">KELANTAN 吉兰丹</option>
<option value="04">MELAKA 马六甲</option>
<option value="05">NEGERI SEMBILAN 森美兰</option>
<option value="06">PAHANG 彭亨</option>
<option value="07">PULAU PINANG 槟城</option>
<option value="08">PERAK 霹雳</option>
<option value="09">PERLIS 玻璃市</option>
<option value="10">SELANGOR 雪兰莪</option>
<option value="11">TERENGGANU 登嘉楼</option>
<option value="12">SABAH 沙巴</option>
<option value="13">SARAWAK 砂拉越</option>
<option value="14">WP KUALA LUMPUR 联邦直辖区吉隆坡</option>
<option value="15">WP LABUAN 联邦直辖区纳闽</option>
<option value="16">WP PUTRAJAYA 联邦直辖区布城</option>
</select>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('visa.hotel_city')}<span>*</span></label>
<div class="layui-input-block">
<select name="hotel_city" id="hotel_city" lay-verify="required">
<option value="">{:lang('visa.hotel_city_placeholder')}</option>
</select>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('visa.hotel_address')}<span>*</span></label>
<div class="layui-input-block">
<input type="text" name="hotel_address" lay-verify="required"
placeholder="{:lang('visa.hotel_address_placeholder')}"
class="layui-input">
<span class="layui-form-mid layui-word-aux">{:lang('visa.copy_hotel')}</span>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('visa.hotel_zip')}<span>*</span></label>
<div class="layui-input-block">
<input type="text" name="hotel_zip" lay-verify="required"
placeholder="{:lang('visa.hotel_zip_placeholder')}"
class="layui-input">
</div>
</div>
</div>
</div>
<div class="layui-row text-center">
<div class="layui-col-xs12">
<div class="layui-form-item">
<button class="layui-btn btn-next" lay-submit>{:lang('visa.next_step')}</button>
</div>
</div>
</div>
</form>
<!-- <form class="layui-form" lay-filter="Form5" id="Form5" action="" autocomplete="off">-->
<!-- <div class="m-form-application-title">-->
<!-- <h2>附件信息</h2>-->
<!-- </div>-->
<!-- <div class="layui-row">-->
<!-- <div class="layui-col-xs12 layui-col-md12">-->
<!-- <div class="layui-form-item">-->
<!-- <label class="layui-form-label">证件照片<span>*</span></label>-->
<!-- <div class="layui-input-block">-->
<!-- <div class="layui-upload">-->
<!-- <button type="button" class="layui-btn" id="person_img">上传图片</button>-->
<!-- <div class="layui-upload-list">-->
<!-- <img class="layui-upload-img" id="person_img_view">-->
<!-- </div>-->
<!-- </div>-->
<!-- <input type="hidden" name="person_img" lay-verify="required">-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="layui-row text-center">-->
<!-- <div class="layui-col-xs12">-->
<!-- <div class="layui-form-item">-->
<!-- <button class="layui-btn btn-next" lay-submit>下一步</button>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </form>-->
<!-- 提交表单 -->
<form class="layui-form" lay-filter="Form3" id="Form3" action="" autocomplete="off">
<div class="m-form-application-title">
<h2>{:lang('visa.applicant_declaration_title')}</h2>
</div>
<div class="layui-row">
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<div class="layui-input-block">
<input type="checkbox" name="check1" checked lay-verify="required|checkboxRequired"
title="{:lang('visa.declaration_statement')}" value="1">
<input type="checkbox" lay-filter="termsCheckbox" name="check2"
title="{:lang('visa.terms_acceptance')}" value="1" checked
lay-verify="required|checkboxRequired">
</div>
</div>
</div>
</div>
<div class="m-form-application-title" style="background: none;color: #000;">
<h2 style="font-size: 16px">{:lang('visa.select_package_title')}</h2>
</div>
<div class="layui-row">
<div class="layui-col-xs12 layui-col-md12">
<div class="layui-form-item">
{volist name="goods" id="vo" key="k"}
<div class="layui-input-block">
<input type="radio" name="total_price" value="{$vo.value}" title="{$vo.desc}"
{if condition="$vo.is_checked eq 1" }
checked="checked"
{/if}
/>
</div>
{/volist}
</div>
</div>
</div>
<!-- <div class="m-form-application-title" style="background: none;color: #000;">-->
<!-- <h2 style="font-size: 16px">{:lang('visa.select_payment_method')}</h2>-->
<!-- </div>-->
<!-- <div class="layui-row">-->
<!-- <div class="layui-col-xs12 layui-col-md12">-->
<!-- <div class="layui-form-item">-->
<!-- <div class="layui-input-block">-->
<!-- <input type="radio" name="pay_type" value="1"-->
<!-- title="{:lang('visa.wechat_pay')}" checked>-->
<!-- <input type="radio" name="pay_type" value="3" title="{:lang('visa.card')}">-->
<!--&lt;!&ndash; <input type="radio" name="pay_type" value="2" title="{:lang('visa.alipay')}">&ndash;&gt;-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<div class="layui-row text-center">
<div class="layui-col-xs12">
<div class="layui-form-item">
<button class="layui-btn" lay-submit lay-filter="formSubmit" id="submit">{:lang('visa.submit_application_button')}
</button>
</div>
</div>
</div>
</form>
</div>
</div>
<div id="confirm" style="display: none">
<table class="layui-table confirm-table">
<colgroup>
<col width="40%">
<col width="60%">
</colgroup>
<tbody>
<tr>
<td>{:lang('visa.confirm_name')}:</td>
<td id="last_name_con"></td>
</tr>
<tr>
<td>{:lang('visa.birth_date')}:</td>
<td id="birth_date_con"></td>
</tr>
<tr>
<td>{:lang('visa.passport_number')}:</td>
<td id="passport_number_con"></td>
</tr>
<tr>
<td>{:lang('visa.passport_expiry')}:</td>
<td id="passport_expiration_date_con"></td>
</tr>
<tr>
<td>{:lang('visa.arrive_date')}:</td>
<td id="arrive_date_con"></td>
</tr>
<tr>
<td>{:lang('visa.leave_date')}:</td>
<td id="leave_date_con"></td>
</tr>
<tr>
<td>{:lang('visa.email_address')}:</td>
<td id="email_con"></td>
</tr>
</tbody>
</table>
<div class="confirm-note">{:lang('visa.verify')}</div>
</div>
{include file="public/newfooter"}
<script>
var lang = {
field_required: "{:lang('js.field_required')}",
alert_message:"{:lang('js.alert_message')}",
notice:"{:lang('js.notice')}",
departure_date_error:"{:lang('js.departure_date_error')}",
failed:"{:lang('js.failed')}",
search_directly:"{:lang('js.search_directly')}",
check_declaration:"{:lang('js.check_declaration')}",
select_one_option:"{:lang('js.select_one_option')}",
choose_package:"{:lang('js.choose_package')}",
english_only:"{:lang('js.english_only')}",
uppercase_only:"{:lang('js.uppercase_only')}",
chinese_only:"{:lang('js.chinese_only')}",
max_100_chars:"{:lang('js.max_100_chars')}",
upload_successful:"{:lang('js.upload_successful')}",
upload_failed:"{:lang('js.upload_failed')}",
upload_error:"{:lang('js.upload_error')}",
verify:"{:lang('js.verify')}",
confirms:"{:lang('js.confirm')}",
edit:"{:lang('js.edit')}",
china_passport_invalid:"{:lang('controller.china_passport_invalid')}",
};
var ocrConfigRaw = '{$ocr_config|default = ''}'.replace(/&quot;/g, '"');
var ocrConfig = {};
var sdk = null;
try {
ocrConfig = ocrConfigRaw ? JSON.parse(ocrConfigRaw) : {};
} catch (e) {
console.error('Failed to parse OCR config', e);
}
if (window.PassportOCRSDK && ocrConfig.base_url && ocrConfig.app_id && ocrConfig.signature && ocrConfig.timestamp) {
try {
sdk = PassportOCRSDK.create({
apiBase: ocrConfig.base_url,
appId: ocrConfig.app_id,
sign: ocrConfig
});
} catch (e) {
console.error('Failed to init OCR sdk', e);
}
}
</script>
<script src="/static/js/mala.js"></script>
<script>
var ismobile = `{$ismobile}`
// 监听复选框点击
form.on('checkbox(termsCheckbox)', function (data) {
var area = [];
if (ismobile == 1) {
area = ['90%', '90%']
} else {
area = ['50%', '60%']
}
if (data.elem.checked) {
// 弹出条款内容的弹窗
layer.open({
type: 1, // 弹窗类型:页面层
title: '{:lang(\'js.terms_privacy_title\')}', content: `
<div style="padding: 20px;">
<h3>{:lang(\'js.terms_conditions\')}</h3>
<p></p><p><span style="font-size: 12px;">{:str_replace(\'%country%\', $country ?? \'\', lang(\'js.terms_intro\'))}</span></p>
<p><span style="font-size: 12px;">{:str_replace(\'%country%\', $country ?? \'\', lang(\'js.our_services\'))}</span></p>
<p><span style="font-size: 12px;">{:str_replace(\'%country%\', $country ?? \'\', lang(\'js.application_process\'))}</span></p>
<p><span style="font-size: 12px;">{:lang(\'js.read_confirmation\')}</span></p>
<p><br></p><p></p>
<h3>{:lang(\'js.privacy_policy\')}</h3>
<p></p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.privacy_intro\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">1. {:lang(\'js.scope\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.scope_content\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">2. {:lang(\'js.information_use\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.information_use_content\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">3. {:lang(\'js.information_disclosure\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.information_disclosure_content\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">4. {:lang(\'js.information_storage\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.information_storage_content\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">5. {:lang(\'js.cookie_use\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.cookie_use_content\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">6. {:lang(\'js.information_security\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.information_security_content\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">7. {:lang(\'js.policy_changes\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.policy_changes_content\')}</p>
<p><br></p><p></p>
</div>
`, area: area, // 弹窗大小
btn: ['关闭'], // 按钮
yes: function (index) {
layer.close(index); // 关闭弹窗
}
});
}
});
function upload(_this) {
var file = _this.files && _this.files[0];
var loadingIndex = null;
var resetFileInput = function () {
$(_this).val('');
};
var closeLoading = function () {
if (loadingIndex !== null) {
layer.close(loadingIndex);
loadingIndex = null;
}
};
var showError = function (msg) {
closeLoading();
layer.msg(msg || '{:lang("js.manual_entry_required")}', {icon: 5});
};
if (!file) {
layer.msg('{:lang("controller.upload_missing")}', {icon: 5});
return false;
}
if (!/image\/\w+/.test(file.type || '')) {
layer.msg("{:lang('js.upload_image_only')}", {icon: 5});
return false;
}
if (!FileReader) {
layer.msg("{:lang('js.browser_not_supported')}", {icon: 5});
return false;
}
var url = "/home/upload/upload_passport";
loadingIndex = layer.load(3, {shade: [0.1]});
compressImage(file, 0.2, async function (compressedFile) {
try {
if (!compressedFile) {
resetFileInput();
showError('{:lang("js.upload_retry")}');
return false;
}
var ocrWarningMsg = '';
if (sdk) {
try {
var ocr_data = await sdk.recognizePassport(compressedFile, {
auto_rotate: true,
return_debug: false
});
if (ocr_data && ocr_data.code === 1 && ocr_data.data) {
var ocr = ocr_data.data || {};
var pad2 = function (n) {
return String(n || '').padStart(2, '0');
};
var buildDate = function (y, m, d) {
if (!y || !m || !d) return '';
return y + '-' + pad2(m) + '-' + pad2(d);
};
$('input[name="last_name"]').val(ocr.last_name || '');
$('input[name="first_name"]').val(ocr.first_name || '');
$('input[name="passport_number"]').val(ocr.passport_number || '');
$('select[name="nation"]').val(ocr.nationality || '');
if (ocr.gender === 'male' || ocr.gender === 'female') {
var gender = ocr.gender === 'male' ? '1' : '2';
$('input[name="gender"][value="' + gender + '"]').prop('checked', true);
}
var expeditionDate = buildDate(ocr.passport_expedition_date_year, ocr.passport_expedition_date_month, ocr.passport_expedition_date_day);
var expirationDate = buildDate(ocr.passport_expiration_date_year, ocr.passport_expiration_date_month, ocr.passport_expiration_date_day);
var birthDate = buildDate(ocr.birth_date_year, ocr.birth_date_month, ocr.birth_date_day);
if (expeditionDate) $('input[name="passport_expedition_date"]').val(expeditionDate);
if (expirationDate) $('input[name="passport_expiration_date"]').val(expirationDate);
if (birthDate) $('input[name="birth_date"]').val(birthDate);
layui.form.render('radio');
layui.form.render('select');
} else {
ocrWarningMsg = '{:lang("js.manual_entry_required")}';
}
} catch (ocrErr) {
ocrWarningMsg = '{:lang("js.manual_entry_required")}';
}
} else {
ocrWarningMsg = '{:lang("js.manual_entry_required")}';
}
closeLoading();
var formData = new FormData();
formData.append('file', compressedFile);
formData.append('path', 'mala');
$.ajax({
url: url,
type: "post",
data: formData,
dataType: "json",
contentType: false,
processData: false,
mimeType: "multipart/form-data",
success: function (res) {
resetFileInput();
if (res.code == 1) {
$('input[name="passport_img"]').val(res.path);
} else {
//ocrWarningMsg = '{:lang("js.upload_retry")}';
}
},
error: function () {
resetFileInput();
//ocrWarningMsg = '{:lang("js.upload_retry")}';
}
});
if (ocrWarningMsg) {
showError(ocrWarningMsg);
}
} catch (e) {
resetFileInput();
showError('{:lang("js.manual_entry_required")}');
}
});
}
function compressImage(file, quality, callback) {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function (event) {
const img = new Image();
img.src = event.target.result;
img.onload = function () {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
let maxWidth = 1200;
let width = img.width;
let height = img.height;
if (width > maxWidth) {
height *= maxWidth / width;
width = maxWidth;
}
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0, width, height);
canvas.toBlob(function (blob) {
const newFile = new File([blob], file.name, { type: file.type });
callback(newFile);
}, file.type, quality);
};
};
}
</script>
</body>
</html>

View File

@@ -0,0 +1,79 @@
{include file="public/head"}
<link rel="stylesheet" href="/static/css/wepay.css?v=1">
<body class="wepay-page">
<div class="zh_content">
{include file="public/newnav"}
</div>
<main class="layui-container wepay-shell">
<section class="wepay-card">
<div class="wepay-card__head">
<div>
<p class="wepay-eyebrow">{:lang('wepay.payment_title')}</p>
<h1>{:lang('wepay.order_success_notice')}</h1>
</div>
<img src="/static/image/zf3.png" alt="{:lang('wepay.payment_title')}">
</div>
<div class="wepay-summary">
<div>
<span>{:lang('wepay.order_number')}</span>
<strong>{$order_sn}</strong>
</div>
<div>
<span>{:lang('wepay.amount_due')}</span>
<strong class="wepay-amount">¥{$total_fee}</strong>
</div>
</div>
<div class="wepay-methods">
<div class="wepay-method">
<div class="wepay-method__title">
<img src="/static/image/wx.png" alt="{:lang('wepay.wechat_pay')}">
<span>{:lang('wepay.wechat_pay')}</span>
</div>
<div class="wepay-qr">
<img src="{$url}" alt="{:lang('wepay.wechat_pay')}">
</div>
</div>
<div class="wepay-method">
<div class="wepay-method__title">
<img src="/static/image/zfb.jpg" alt="{:lang('wepay.alipay')}">
<span>{:lang('wepay.alipay')}</span>
</div>
<div class="wepay-qr">
<img src="{$aliurl}" alt="{:lang('wepay.alipay')}">
</div>
</div>
<aside class="wepay-guide">
<img src="/static/image/saoyisao.png" alt="{:lang('wepay.scan_to_pay')}">
<h2>{:lang('wepay.scan_to_pay')}</h2>
<p>{:lang('wepay.scan_notice')}</p>
<div class="wepay-waiting">{:lang('wepay.processing')}</div>
</aside>
</div>
</section>
</main>
{include file="public/newfooter" }
<script>
var interval = setInterval(function () {
layui.$.ajax({
type: "POST",
url: "{:url('wepay/pcresult', ['order_sn' => $order_sn])}",
dataType: "json",
success: function (data) {
if (data.status == 1) {
clearInterval(interval);
location.href = data.url;
}
}
});
}, 1000);
</script>
</body>
</html>

View File

@@ -0,0 +1,154 @@
{include file="public/head"}
<link rel="stylesheet" href="/static/css/wepay.css?v=2">
</head>
<body class="wepay-page">
<div class="zh_content">
{include file="public/newnav" }
</div>
<main
class="container wepay-shell"
id="payment-success-page"
data-payment-confirmed="{$payment_confirmed}"
data-poll-status-url="{$poll_status_url}"
>
<section class="wepay-result-card">
<div id="payment-pending-block" class="payment-pending" style="display: none;">
<div class="wepay-result-icon wepay-result-icon--pending">
<span class="payment-spinner"></span>
</div>
<p class="wepay-eyebrow">{:lang('wepay.payment_title')}</p>
<h1 class="payment-pending-title">{:lang('sqpay.notice_title')}</h1>
<p class="payment-pending-text">{:lang('sqpay.notice_text')}</p>
<p class="payment-pending-order">
{:str_replace('%order_sn%', $order_sn ?? '', lang('pay.order_submitted'))}
</p>
<p class="payment-pending-status" id="payment-pending-status">{:lang('sqpay.notice_text')}</p>
<div class="wepay-actions">
<a class="wepay-button wepay-button--ghost" href="{$repay_url}">{:lang('pay.repay_button')}</a>
</div>
</div>
<div id="payment-success-block" class="wepay-success-detail">
<div class="wepay-result-icon wepay-result-icon--success">&check;</div>
<p class="wepay-eyebrow">{:lang('wepay.payment_success')}</p>
<h1>{:lang('pay.thank_you_message')}</h1>
<p class="wepay-success-order">
{:str_replace('%order_sn%', $order_sn ?? '', lang('pay.order_submitted'))}
</p>
<p class="wepay-success-text">
{:str_replace('%country%', $country ?? '', lang('pay.processing_time_info'))}
</p>
{if $arrive_date_info}
<p class="wepay-success-text">
{$arrive_date_info}
</p>
{/if}
<p class="wepay-contact-text">
{:lang('pay.contact')}
</p>
</div>
<div class="wepay-actions" id="payment-success-action">
<a class="wepay-button" href="{:url('visa/index')}">{:lang('pay.visa_application_button')}</a>
</div>
</section>
</main>
{include file="public/newfooter" }
<script>
(function () {
var pageEl = document.getElementById('payment-success-page');
if (!pageEl) {
return;
}
var isConfirmed = pageEl.getAttribute('data-payment-confirmed') === '1';
var pollUrl = pageEl.getAttribute('data-poll-status-url');
var pendingBlock = document.getElementById('payment-pending-block');
var successBlock = document.getElementById('payment-success-block');
var successAction = document.getElementById('payment-success-action');
var pendingStatus = document.getElementById('payment-pending-status');
var pendingText = "{:lang('sqpay.notice_text')}";
var successText = "{:lang('controller.payment_success')}";
var pollTimer = null;
var maxAttempts = 30;
var attempt = 0;
function showPending() {
if (pendingBlock) {
pendingBlock.style.display = 'block';
}
if (successBlock) {
successBlock.style.display = 'none';
}
if (successAction) {
successAction.style.display = 'none';
}
}
function showSuccess() {
if (pendingBlock) {
pendingBlock.style.display = 'none';
}
if (successBlock) {
successBlock.style.display = 'block';
}
if (successAction) {
successAction.style.display = 'block';
}
}
if (isConfirmed || !pollUrl) {
showSuccess();
return;
}
showPending();
async function pollStatus() {
attempt++;
try {
var response = await fetch(pollUrl, {
method: 'GET',
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
});
var data = await response.json();
if (data.status === 1) {
if (pendingStatus) {
pendingStatus.textContent = successText;
}
window.clearInterval(pollTimer);
showSuccess();
return;
}
if (data.status === -1 && data.url) {
window.clearInterval(pollTimer);
window.location.href = data.url;
return;
}
if (pendingStatus) {
pendingStatus.textContent = data.msg || pendingText;
}
} catch (e) {
if (pendingStatus) {
pendingStatus.textContent = pendingText;
}
}
if (attempt >= maxAttempts) {
window.clearInterval(pollTimer);
if (pendingStatus) {
pendingStatus.textContent = pendingText;
}
}
}
pollStatus();
pollTimer = window.setInterval(pollStatus, 2000);
})();
</script>
</body>
</html>